diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I index 0f30621b01..6f6db0676f 100644 --- a/panda/src/display/displayRegion.I +++ b/panda/src/display/displayRegion.I @@ -102,6 +102,17 @@ get_camera() const { return _camera; } +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_qpcamera +// Access: Public +// Description: Returns the camera associated with this +// DisplayRegion, or NULL if no camera is associated. +//////////////////////////////////////////////////////////////////// +INLINE const NodeChain &DisplayRegion:: +get_qpcamera() const { + return _qpcamera; +} + //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::set_cull_frustum // Access: Public diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index 49cd5ceb45..20a7b4f40d 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -22,6 +22,7 @@ #include "graphicsWindow.h" #include "config_display.h" #include "displayRegion.h" +#include "qpcamera.h" //////////////////////////////////////////////////////////////////// @@ -33,6 +34,7 @@ DisplayRegion:: DisplayRegion(GraphicsLayer *layer) : _l(0.), _r(1.), _b(0.), _t(1.), _layer(layer), + _camera_node((qpCamera *)NULL), _active(true) { } @@ -44,10 +46,11 @@ DisplayRegion(GraphicsLayer *layer) : //////////////////////////////////////////////////////////////////// DisplayRegion:: DisplayRegion(GraphicsLayer *layer, const float l, - const float r, const float b, const float t) - : _l(l), _r(r), _b(b), _t(t), - _layer(layer), - _active(true) + const float r, const float b, const float t) : + _l(l), _r(r), _b(b), _t(t), + _layer(layer), + _camera_node((qpCamera *)NULL), + _active(true) { } @@ -62,7 +65,8 @@ DisplayRegion:: DisplayRegion(int xsize, int ysize) : _l(0.), _r(1.), _b(0.), _t(1.), _pl(0), _pr(xsize), _pb(0), _pt(ysize), - _layer((GraphicsLayer *)0L), + _layer((GraphicsLayer *)NULL), + _camera_node((qpCamera *)NULL), _active(true) { } @@ -74,8 +78,7 @@ DisplayRegion(int xsize, int ysize) : //////////////////////////////////////////////////////////////////// DisplayRegion:: DisplayRegion(const DisplayRegion&) { - display_cat.error() - << "DisplayRegions should not be copied" << endl; + nassertv(false); } //////////////////////////////////////////////////////////////////// @@ -83,11 +86,9 @@ DisplayRegion(const DisplayRegion&) { // Access: Private // Description: //////////////////////////////////////////////////////////////////// -DisplayRegion &DisplayRegion:: -operator=(const DisplayRegion&) { - display_cat.error() - << "DisplayRegions should not be assigned" << endl; - return *this; +void DisplayRegion:: +operator = (const DisplayRegion&) { + nassertv(false); } //////////////////////////////////////////////////////////////////// @@ -97,6 +98,7 @@ operator=(const DisplayRegion&) { //////////////////////////////////////////////////////////////////// DisplayRegion:: ~DisplayRegion() { + set_qpcamera(NodeChain()); } //////////////////////////////////////////////////////////////////// @@ -182,6 +184,41 @@ set_camera(Camera *camera) { set_cull_frustum(camera); } +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::set_qpcamera +// Access: Public +// Description: Sets the camera that is associated with this +// DisplayRegion. There is a one-to-one association +// between cameras and DisplayRegions; if this camera +// was already associated with a different +// DisplayRegion, that association is removed. +// +// The camera is actually set via a NodeChain, which +// clarifies which instance of the camera (if there +// happen to be multiple instances) we should use. +//////////////////////////////////////////////////////////////////// +void DisplayRegion:: +set_qpcamera(const NodeChain &camera) { + qpCamera *camera_node = (qpCamera *)NULL; + if (!camera.is_empty()) { + DCAST_INTO_V(camera_node, camera.node()); + } + + if (camera_node != _camera_node) { + if (_camera_node != (qpCamera *)NULL) { + // We need to tell the old camera we're not using him anymore. + _camera_node->remove_display_region(this); + } + _camera_node = camera_node; + if (_camera_node != (qpCamera *)NULL) { + // Now tell the new camera we are using him. + _camera_node->add_display_region(this); + } + } + + _qpcamera = camera; +} + //////////////////////////////////////////////////////////////////// // Function: DisplayRegion::output // Access: Public diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h index d819bb4ab9..a363ccf00a 100644 --- a/panda/src/display/displayRegion.h +++ b/panda/src/display/displayRegion.h @@ -17,25 +17,21 @@ //////////////////////////////////////////////////////////////////// #ifndef DISPLAYREGION_H #define DISPLAYREGION_H -// -//////////////////////////////////////////////////////////////////// -// Includes -//////////////////////////////////////////////////////////////////// -#include -#include -#include +#include "pandabase.h" + +#include "referenceCount.h" +#include "camera.h" +#include "nodeChain.h" #include "plist.h" -//////////////////////////////////////////////////////////////////// -// Defines -//////////////////////////////////////////////////////////////////// class GraphicsLayer; class GraphicsChannel; class GraphicsWindow; class GraphicsPipe; class CullHandler; +class qpCamera; //////////////////////////////////////////////////////////////////// // Class : DisplayRegion @@ -48,7 +44,12 @@ public: const float l, const float r, const float b, const float t); DisplayRegion(int xsize, int ysize); - virtual ~DisplayRegion(); +private: + DisplayRegion(const DisplayRegion &); + void operator = (const DisplayRegion &); + +public: + ~DisplayRegion(); PUBLISHED: INLINE void get_dimensions(float &l, float &r, float &b, float &t) const; @@ -66,6 +67,9 @@ PUBLISHED: void set_camera(Camera *camera); INLINE Camera *get_camera() const; + void set_qpcamera(const NodeChain &camera); + INLINE const NodeChain &get_qpcamera() const; + INLINE void set_cull_frustum(LensNode *cull_frustum); INLINE LensNode *get_cull_frustum() const; @@ -98,14 +102,12 @@ protected: GraphicsLayer *_layer; PT(Camera) _camera; + NodeChain _qpcamera; + qpCamera *_camera_node; PT(LensNode) _cull_frustum; bool _active; -private: - DisplayRegion(const DisplayRegion &); - DisplayRegion& operator=(const DisplayRegion &); - friend class GraphicsLayer; }; diff --git a/panda/src/display/drawCullHandler.cxx b/panda/src/display/drawCullHandler.cxx index 56e82b3c4e..99f7bcc17f 100644 --- a/panda/src/display/drawCullHandler.cxx +++ b/panda/src/display/drawCullHandler.cxx @@ -18,6 +18,7 @@ #include "drawCullHandler.h" #include "geom.h" +#include "transformState.h" #include "renderState.h" #include "graphicsStateGuardian.h" @@ -30,7 +31,9 @@ // discovered by the CullTraverser. //////////////////////////////////////////////////////////////////// void DrawCullHandler:: -record_geom(Geom *geom, const RenderState *state) { +record_geom(Geom *geom, const TransformState *transform, + const RenderState *state) { + _gsg->set_transform(transform); _gsg->set_state(state); geom->draw(_gsg); } diff --git a/panda/src/display/drawCullHandler.h b/panda/src/display/drawCullHandler.h index 430f49eddf..6ebccef3eb 100644 --- a/panda/src/display/drawCullHandler.h +++ b/panda/src/display/drawCullHandler.h @@ -40,7 +40,8 @@ public: INLINE DrawCullHandler(GraphicsStateGuardian *gsg); // virtual void begin_decal(); - virtual void record_geom(Geom *geom, const RenderState *state); + virtual void record_geom(Geom *geom, const TransformState *transform, + const RenderState *state); // virtual void push_decal(); // virtual void pop_decal(); diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index e04b848984..1bbd5704fa 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -99,6 +99,7 @@ cull_and_draw_together() { cull_and_draw_together(win, dr); } win->flip(); + win->process_events(); } } @@ -111,19 +112,27 @@ cull_and_draw_together() { //////////////////////////////////////////////////////////////////// void GraphicsEngine:: cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) { - Camera *camera = dr->get_camera(); - if (camera == (Camera *)NULL || !camera->is_active()) { + const NodeChain &camera = dr->get_qpcamera(); + if (camera.is_empty()) { // No camera, no draw. return; } - Lens *lens = camera->get_lens(); + qpCamera *camera_node; + DCAST_INTO_V(camera_node, camera.node()); + + if (!camera_node->is_active()) { + // Camera inactive, no draw. + return; + } + + Lens *lens = camera_node->get_lens(); if (lens == (Lens *)NULL) { // No lens, no draw. return; } - PandaNode *scene = camera->get_qpscene(); + PandaNode *scene = camera_node->get_scene(); if (scene == (PandaNode *)NULL) { // No scene, no draw. return; @@ -143,9 +152,7 @@ cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) { DrawCullHandler cull_handler(gsg); qpCullTraverser trav; trav.set_cull_handler(&cull_handler); - - // Here we should figure out the world transform: the camera's - // inverse transform. + trav.set_world_transform(camera.get_rel_transform(NodeChain())); DisplayRegionStack old_dr = gsg->push_display_region(dr); gsg->prepare_display_region(); diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 8564780609..d97d0677d8 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -144,6 +144,22 @@ set_state(const RenderState *state) { _qpstate = _qpstate->issue_delta_set(state, this); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_transform +// Access: Public +// Description: Sets the world transform that will be applied to +// subsequent geometry. This is normally called only +// during the draw process, immediately before issuing +// geometry commands. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsStateGuardian:: +set_transform(const TransformState *transform) { + if (transform != _transform) { + _transform = transform; + issue_transform(transform); + } +} + /* //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_state diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index cad320d6ad..5ef348688f 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -879,6 +879,16 @@ void GraphicsStateGuardian:: end_decal(GeomNode *) { } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::issue_transform +// Access: Public, Virtual +// Description: Sends the indicated transform matrix to the graphics +// API to be applied to future vertices. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +issue_transform(const TransformState *) { +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::issue_color // Access: Public, Virtual diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 3984c2e467..8428c018ff 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -35,6 +35,7 @@ #include "renderTraverser.h" #include "pStatCollector.h" #include "allTransitionsWrapper.h" +#include "transformState.h" #include "renderState.h" #include "notify.h" @@ -142,6 +143,7 @@ public: INLINE void modify_state(const RenderState *state); INLINE void set_state(const RenderState *state); + INLINE void set_transform(const TransformState *transform); RenderBuffer get_render_buffer(int buffer_type); @@ -173,6 +175,7 @@ public: INLINE void clear_cached_state(void) { _state.clear(); }; + virtual void issue_transform(const TransformState *transform); virtual void issue_color(const ColorAttrib *attrib); protected: @@ -224,6 +227,7 @@ protected: State _state; CPT(RenderState) _qpstate; + CPT(TransformState) _transform; int _buffer_mask; Colorf _color_clear_value; diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index 59bcf40c86..1e1a778ede 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -3415,6 +3415,24 @@ issue_polygon_offset(const PolygonOffsetTransition *attrib) { report_errors(); } +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_transform +// Access: Public, Virtual +// Description: Sends the indicated transform matrix to the graphics +// API to be applied to future vertices. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_transform(const TransformState *transform) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(transform->get_mat().get_data()); + + report_errors(); +} + //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::issue_texture // Access: Public, Virtual diff --git a/panda/src/glgsg/glGraphicsStateGuardian.h b/panda/src/glgsg/glGraphicsStateGuardian.h index 2d1110fc6c..cca731bfc6 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.h +++ b/panda/src/glgsg/glGraphicsStateGuardian.h @@ -156,6 +156,7 @@ public: virtual void issue_point_shape(const PointShapeTransition *attrib); virtual void issue_polygon_offset(const PolygonOffsetTransition *attrib); + virtual void issue_transform(const TransformState *transform); virtual void issue_texture(const TextureAttrib *attrib); virtual bool wants_normals(void) const; diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx index 38f5d4ca47..c258de071b 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.cxx +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -1217,6 +1217,9 @@ void glxGraphicsWindow::process_events(void) int got_event; glxGraphicsWindow* window; + if (_change_mask) + handle_changes(); + glxDisplay *glx = _pipe->get_glx_display(); nassertv(glx != (glxDisplay *)NULL); diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index 9e2b9cbdf3..a337bd0f4c 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -1,11 +1,12 @@ #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ dtoolutil:c dtoolbase:c dtool:m -#define LOCAL_LIBS gsgbase gobj putil graph linmath express pandabase +#define LOCAL_LIBS event gsgbase gobj putil graph linmath express pandabase #begin lib_target #define TARGET pgraph #define SOURCES \ + qpcamera.h qpcamera.I \ colorAttrib.h colorAttrib.I \ config_pgraph.h \ cullHandler.h \ @@ -14,6 +15,9 @@ cycleDataReader.h cycleDataReader.I \ cycleDataWriter.h cycleDataWriter.I \ qpgeomNode.h qpgeomNode.I \ + qplensNode.h qplensNode.I \ + nodeChain.h nodeChain.I \ + nodeChainComponent.h nodeChainComponent.I \ pandaNode.h pandaNode.I \ pipeline.h pipeline.I \ pipelineCycler.h pipelineCycler.I \ @@ -21,10 +25,11 @@ renderAttrib.h renderAttrib.I \ renderState.h renderState.I \ textureAttrib.h textureAttrib.I \ - transformAttrib.h transformAttrib.I + transformState.h transformState.I #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define INCLUDED_SOURCES \ + qpcamera.cxx \ colorAttrib.cxx \ config_pgraph.cxx \ cullHandler.cxx \ @@ -33,6 +38,9 @@ cycleDataReader.cxx \ cycleDataWriter.cxx \ qpgeomNode.cxx \ + qplensNode.cxx \ + nodeChain.cxx \ + nodeChainComponent.cxx \ pandaNode.cxx \ pipeline.cxx \ pipelineCycler.cxx \ @@ -40,7 +48,7 @@ renderAttrib.cxx \ renderState.cxx \ textureAttrib.cxx \ - transformAttrib.cxx + transformState.cxx #if $[DONT_COMBINE_PGRAPH] #define SOURCES $[SOURCES] $[INCLUDED_SOURCES] @@ -49,6 +57,7 @@ #endif #define INSTALL_HEADERS \ + qpcamera.h qpcamera.I \ colorAttrib.h colorAttrib.I \ config_pgraph.h \ cullHandler.h \ @@ -57,6 +66,9 @@ cycleDataReader.h cycleDataReader.I \ cycleDataWriter.h cycleDataWriter.I \ qpgeomNode.h qpgeomNode.I \ + qplensNode.h qplensNode.I \ + nodeChain.h nodeChain.I \ + nodeChainComponent.h nodeChainComponent.I \ pandaNode.h pandaNode.I \ pipeline.h pipeline.I \ pipelineCycler.h pipelineCycler.I \ @@ -64,7 +76,7 @@ renderAttrib.h renderAttrib.I \ renderState.h renderState.I \ textureAttrib.h textureAttrib.I \ - transformAttrib.h transformAttrib.I + transformState.h transformState.I #define IGATESCAN all diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 1e2c6c7fd2..a53b2c739f 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -18,13 +18,18 @@ #include "config_pgraph.h" +#include "qpcamera.h" #include "colorAttrib.h" #include "qpgeomNode.h" +#include "qplensNode.h" +#include "nodeChain.h" +#include "nodeChainComponent.h" #include "pandaNode.h" #include "renderAttrib.h" #include "renderState.h" #include "textureAttrib.h" -#include "transformAttrib.h" +#include "colorAttrib.h" +#include "transformState.h" #include "dconfig.h" @@ -52,18 +57,24 @@ init_libpgraph() { } initialized = true; + qpCamera::init_type(); ColorAttrib::init_type(); qpGeomNode::init_type(); + qpLensNode::init_type(); + NodeChain::init_type(); + NodeChainComponent::init_type(); PandaNode::init_type(); RenderAttrib::init_type(); RenderState::init_type(); TextureAttrib::init_type(); - TransformAttrib::init_type(); + ColorAttrib::init_type(); + TransformState::init_type(); ColorAttrib::register_with_read_factory(); qpGeomNode::register_with_read_factory(); PandaNode::register_with_read_factory(); RenderState::register_with_read_factory(); TextureAttrib::register_with_read_factory(); - TransformAttrib::register_with_read_factory(); + ColorAttrib::register_with_read_factory(); + TransformState::register_with_read_factory(); } diff --git a/panda/src/pgraph/cullHandler.cxx b/panda/src/pgraph/cullHandler.cxx index 41ff6061c6..d6b1b05c65 100644 --- a/panda/src/pgraph/cullHandler.cxx +++ b/panda/src/pgraph/cullHandler.cxx @@ -18,6 +18,7 @@ #include "cullHandler.h" #include "geom.h" +#include "transformState.h" #include "renderState.h" //////////////////////////////////////////////////////////////////// @@ -31,6 +32,7 @@ // it's not intended to be used except for debugging. //////////////////////////////////////////////////////////////////// void CullHandler:: -record_geom(Geom *geom, const RenderState *state) { - cerr << *geom << " " << *state << "\n"; +record_geom(Geom *geom, const TransformState *transform, + const RenderState *state) { + cerr << *geom << " " << *transform << " " << *state << "\n"; } diff --git a/panda/src/pgraph/cullHandler.h b/panda/src/pgraph/cullHandler.h index b7e16f6057..f4c62f9c7b 100644 --- a/panda/src/pgraph/cullHandler.h +++ b/panda/src/pgraph/cullHandler.h @@ -22,6 +22,7 @@ #include "pandabase.h" class Geom; +class TransformState; class RenderState; //////////////////////////////////////////////////////////////////// @@ -34,7 +35,8 @@ class RenderState; class EXPCL_PANDA CullHandler { public: // virtual void begin_decal(); - virtual void record_geom(Geom *geom, const RenderState *state); + virtual void record_geom(Geom *geom, const TransformState *transform, + const RenderState *state); // virtual void push_decal(); // virtual void pop_decal(); }; diff --git a/panda/src/pgraph/cycleDataReader.I b/panda/src/pgraph/cycleDataReader.I index f62d0d6ead..77d82360bb 100644 --- a/panda/src/pgraph/cycleDataReader.I +++ b/panda/src/pgraph/cycleDataReader.I @@ -27,7 +27,7 @@ INLINE CycleDataReader:: CycleDataReader(const PipelineCycler &cycler) : _cycler(cycler) { - _pointer = (const CycleDataType *)_cycler.read(); + _pointer = _cycler.read(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/cycleDataWriter.I b/panda/src/pgraph/cycleDataWriter.I index b7dc4a7597..c10d211dba 100644 --- a/panda/src/pgraph/cycleDataWriter.I +++ b/panda/src/pgraph/cycleDataWriter.I @@ -27,7 +27,7 @@ INLINE CycleDataWriter:: CycleDataWriter(PipelineCycler &cycler) : _cycler(cycler) { - _pointer = (CycleDataType *)_cycler.write(); + _pointer = _cycler.write(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/nodeChain.I b/panda/src/pgraph/nodeChain.I new file mode 100644 index 0000000000..d48b871664 --- /dev/null +++ b/panda/src/pgraph/nodeChain.I @@ -0,0 +1,323 @@ +// Filename: nodeChain.I +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::Default Constructor +// Access: Published +// Description: This constructs an empty NodeChain with no nodes. It +// cannot be extended, since you cannot add nodes without +// first specifying the top node. Use the constructor +// that receives a node if you ever want to do anything +// with this chain. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain:: +NodeChain() : + _error_type(ET_ok) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::Constructor +// Access: Published +// Description: This constructs an empty NodeChain with a single node. +// This chain may now be extended by repeatedly calling +// push_back() with each node below that node in +// sequence. +// +// If the Node pointer is NULL, this quietly creates an +// empty NodeChain. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain:: +NodeChain(PandaNode *top_node) : + _error_type(ET_ok) +{ + if (top_node != (PandaNode *)NULL) { + _head = top_node->get_generic_component(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeChain:: +NodeChain(const NodeChain ©) : + _head(copy._head), + _error_type(copy._error_type) +{ + uncollapse_head(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeChain:: +operator = (const NodeChain ©) { + _head = copy._head; + _error_type = copy._error_type; + uncollapse_head(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::not_found named constructor +// Access: Published, Static +// Description: Creates a NodeChain with the ET_not_found error type +// set. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain NodeChain:: +not_found() { + NodeChain result; + result._error_type = ET_not_found; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::removed named constructor +// Access: Published, Static +// Description: Creates a NodeChain with the ET_removed error type +// set. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain NodeChain:: +removed() { + NodeChain result; + result._error_type = ET_removed; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::fail named constructor +// Access: Published, Static +// Description: Creates a NodeChain with the ET_fail error type +// set. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain NodeChain:: +fail() { + NodeChain result; + result._error_type = ET_fail; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::is_empty +// Access: Published +// Description: Returns true if the NodeChain contains no nodes. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +is_empty() const { + return (_head == (NodeChainComponent *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::is_singleton +// Access: Published +// Description: Returns true if the NodeChain contains exactly one +// node. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +is_singleton() const { + uncollapse_head(); + return (_head != (NodeChainComponent *)NULL && _head->is_top_node()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_error_type +// Access: Published +// Description: If is_empty() is true, this returns a code that +// represents the reason why the NodeChain is empty. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain::ErrorType NodeChain:: +get_error_type() const { + return _error_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::node +// Access: Published +// Description: Returns the bottom node of the path, or NULL if the +// path is empty. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *NodeChain:: +node() const { + if (is_empty()) { + return (PandaNode *)NULL; + } + return _head->get_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::has_parent +// Access: Published +// Description: Returns true if the node at the bottom of the +// NodeChain has a parent; i.e. the NodeChain contains at +// least two nodes. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +has_parent() const { + return !is_empty() && !is_singleton(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_parent +// Access: Published +// Description: Returns the NodeChain to the parent of the bottom +// node: that is, this NodeChain, shortened by one node. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain NodeChain:: +get_parent() const { + nassertr(has_parent(), NodeChain::fail()); + NodeChain parent(*this); + parent._head = parent._head->get_next(); + return parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::attach_new_node +// Access: Published +// Description: Creates an ordinary PandaNode and attaches it below +// the current NodeChain, returning a new NodeChain that +// references it. +//////////////////////////////////////////////////////////////////// +INLINE NodeChain NodeChain:: +attach_new_node(const string &name, int sort) const { + nassertr(verify_complete(), NodeChain::fail()); + nassertr(!is_empty(), *this); + + return attach_new_node(new PandaNode(name), sort); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_net_state +// Access: Published +// Description: Returns the net state on this node from the root. +//////////////////////////////////////////////////////////////////// +INLINE CPT(RenderState) NodeChain:: +get_net_state() const { + uncollapse_head(); + return r_get_net_state(_head); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_net_transform +// Access: Published +// Description: Returns the net transform on this node from the root. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) NodeChain:: +get_net_transform() const { + uncollapse_head(); + return r_get_net_transform(_head); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_mat +// Access: Published +// Description: Returns the matrix that describes the coordinate +// space of this node, relative to the other +// node's coordinate space. If either NodeChain is +// empty, it is taken to indicate the top of the graph. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &NodeChain:: +get_mat(const NodeChain &other) const { + CPT(TransformState) transform = get_rel_transform(other); + // We can safely assume the transform won't go away when the + // function returns, since its reference count is also held in the + // cache. This assumption allows us to return a reference to the + // matrix, instead of having to return a matrix on the stack. + nassertr(transform->get_ref_count() > 1, LMatrix4f::ident_mat()); + return transform->get_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::operator == +// Access: Published +// Description: Returns true if the two chains are equivalent; that +// is, if they contain the same list of nodes in the same +// order. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +operator == (const NodeChain &other) const { + return (compare_to(other) == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::operator != +// Access: Published +// Description: Returns true if the two chains are not equivalent. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +operator != (const NodeChain &other) const { + return (compare_to(other) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::operator < +// Access: Published +// Description: Returns true if this NodeChain sorts before the other +// one, false otherwise. The sorting order of two +// nonequivalent NodeChains is consistent but undefined, +// and is useful only for storing NodeChains in a sorted +// container like an STL set. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChain:: +operator < (const NodeChain &other) const { + return (compare_to(other) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::compare_to +// Access: Published +// Description: Returns a number less than zero if this NodeChain +// sorts before the other one, greater than zero if it +// sorts after, or zero if they are equivalent. +// +// Two NodeChains are considered equivalent if they +// consist of exactly the same list of nodes in the same +// order. Otherwise, they are different; different +// NodeChains will be ranked in a consistent but +// undefined ordering; the ordering is useful only for +// placing the NodeChains in a sorted container like an +// STL set. +//////////////////////////////////////////////////////////////////// +INLINE int NodeChain:: +compare_to(const NodeChain &other) const { + return r_compare_to(_head, other._head); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::output +// Access: Published +// Description: Writes a sensible description of the NodeChain to the +// indicated output stream. +//////////////////////////////////////////////////////////////////// +INLINE void NodeChain:: +output(ostream &out) const { + if (_head == (NodeChainComponent *)NULL) { + out << "(empty)"; + } else { + r_output(out, _head); + } +} + +INLINE ostream &operator << (ostream &out, const NodeChain &node_chain) { + node_chain.output(out); + return out; +} + diff --git a/panda/src/pgraph/nodeChain.cxx b/panda/src/pgraph/nodeChain.cxx new file mode 100644 index 0000000000..7d76df31fe --- /dev/null +++ b/panda/src/pgraph/nodeChain.cxx @@ -0,0 +1,514 @@ +// Filename: nodeChain.cxx +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "nodeChain.h" +#include "node.h" +#include "namedNode.h" +#include "config_pgraph.h" +#include "plist.h" + +TypeHandle NodeChain::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_num_nodes +// Access: Published +// Description: Returns the number of nodes in the path. +//////////////////////////////////////////////////////////////////// +int NodeChain:: +get_num_nodes() const { + if (is_empty()) { + return 0; + } + uncollapse_head(); + return _head->get_length(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_node +// Access: Published +// Description: Returns the nth node of the path, where 0 is the +// bottom node and get_num_nodes() - 1 is the top node. +// This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +PandaNode *NodeChain:: +get_node(int index) const { + nassertr(index >= 0 && index < get_num_nodes(), NULL); + + uncollapse_head(); + NodeChainComponent *comp = _head; + while (index > 0) { + // If this assertion fails, the index was out of range; the + // component's length must have been invalid. + nassertr(comp != (NodeChainComponent *)NULL, NULL); + comp = comp->get_next(); + index--; + } + + // If this assertion fails, the index was out of range; the + // component's length must have been invalid. + nassertr(comp != (NodeChainComponent *)NULL, NULL); + return comp->get_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_top_node +// Access: Published +// Description: Returns the top node of the path, or NULL if the path +// is empty. This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +PandaNode *NodeChain:: +get_top_node() const { + if (is_empty()) { + return (PandaNode *)NULL; + } + + uncollapse_head(); + NodeChainComponent *comp = _head; + while (!comp->is_top_node()) { + comp = comp->get_next(); + nassertr(comp != (NodeChainComponent *)NULL, NULL); + } + + return comp->get_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::reparent_to +// Access: Published +// Description: Removes the bottom node of the NodeChain from its +// current parent and attaches it to the bottom node of +// the indicated NodeChain. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +reparent_to(const NodeChain &other, int sort) { + nassertv(other.verify_complete()); + nassertv_always(!is_empty()); + nassertv_always(!other.is_empty()); + + uncollapse_head(); + other.uncollapse_head(); + PandaNode::reparent(other._head, _head, sort); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::wrt_reparent_to +// Access: Published +// Description: This functions identically to reparent_to(), except +// the transform on this node is also adjusted so that +// the node remains in the same place in world +// coordinates, even if it is reparented into a +// different coordinate system. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +wrt_reparent_to(const NodeChain &other, int sort) { + nassertv(other.verify_complete()); + nassertv_always(!is_empty()); + nassertv_always(!other.is_empty()); + + //**** + /* + LMatrix4f mat = get_mat(other); + set_mat(mat); + */ + + reparent_to(other, sort); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::instance_to +// Access: Published +// Description: Adds the bottom node of the NodeChain as a child of +// the bottom node of the indicated other NodeChain. Any +// other parent-child relations of the node are +// unchanged; in particular, the node is not removed +// from its existing parent, if any. +// +// If the node already had an existing parent, this +// method will create a new instance of the node within +// the scene graph. +// +// This does not change the NodeChain itself, but does +// return a new NodeChain that reflects the new instance +// node. +//////////////////////////////////////////////////////////////////// +NodeChain NodeChain:: +instance_to(const NodeChain &other, int sort) const { + nassertr(verify_complete(), NodeChain::fail()); + nassertr(!is_empty(), NodeChain::fail()); + nassertr(!other.is_empty(), NodeChain::fail()); + + uncollapse_head(); + other.uncollapse_head(); + + NodeChain new_instance; + new_instance._head = PandaNode::attach(other._head, node(), sort); + + return new_instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::copy_to +// Access: Published +// Description: Functions exactly like instance_to(), except a deep +// copy is made of the bottom node and all of its +// descendents, which is then parented to the indicated +// node. A NodeChain to the newly created copy is +// returned. +// +// Certain kinds of nodes may not be copied; if one of +// these is encountered, the node will be "copied" as +// the nearest copyable base class. For instance, a +// Camera node in the graph will become a simple +// NamedNode. +//////////////////////////////////////////////////////////////////// +NodeChain NodeChain:: +copy_to(const NodeChain &other, int sort) const { + //***** + return instance_to(other, sort); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::attach_new_node +// Access: Published +// Description: Attaches a new node, with or without existing +// parents, to the scene graph below the bottom node of +// this NodeChain. This is the preferred way to add +// nodes to the graph. +// +// This does *not* automatically extend the current +// NodeChain to reflect the attachment; however, a +// NodeChain that does reflect this extension is +// returned. +//////////////////////////////////////////////////////////////////// +NodeChain NodeChain:: +attach_new_node(PandaNode *node, int sort) const { + nassertr(verify_complete(), NodeChain::fail()); + nassertr(!is_empty(), NodeChain()); + nassertr(node != (PandaNode *)NULL, NodeChain()); + + uncollapse_head(); + NodeChain new_path(*this); + new_path._head = PandaNode::attach(_head, node, sort); + return new_path; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::remove_node +// Access: Published +// Description: Disconnects the bottom node from the scene graph. +// This will also delete the node if there are no other +// pointers to it. +// +// Normally, this should be called only when you are +// really done with the node. If you want to remove a +// node from the scene graph but keep it around for +// later, you should probably use reparent_to() and put +// it under a holding node instead. +// +// After the node is removed, the NodeChain will have +// been cleared. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +remove_node() { + nassertv(_error_type != ET_not_found); + if (is_empty()) { + // If we have no arcs (maybe we were already removed), quietly do + // nothing except to ensure the NodeChain is clear. + (*this) = NodeChain::removed(); + return; + } + + uncollapse_head(); + PandaNode::detach(_head); + + (*this) = NodeChain::removed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_rel_state +// Access: Published +// Description: Returns the state changes that must be made to +// transition from the render state of this node to the +// render state of the other node. +//////////////////////////////////////////////////////////////////// +CPT(RenderState) NodeChain:: +get_rel_state(const NodeChain &other) const { + if (is_empty()) { + return other.get_net_state(); + } + if (other.is_empty()) { + return get_net_state()->invert_compose(RenderState::make_empty()); + } + + nassertr(verify_complete(), RenderState::make_empty()); + nassertr(other.verify_complete(), RenderState::make_empty()); + + int a_count, b_count; + find_common_ancestor(*this, other, a_count, b_count); + + CPT(RenderState) a_state = r_get_partial_state(_head, a_count); + CPT(RenderState) b_state = r_get_partial_state(other._head, b_count); + return a_state->invert_compose(b_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::get_rel_transform +// Access: Published +// Description: Returns the relative transform from this node to the +// other node; i.e. the transformation of the other node +// as seen from this node. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) NodeChain:: +get_rel_transform(const NodeChain &other) const { + if (is_empty()) { + return other.get_net_transform(); + } + if (other.is_empty()) { + return get_net_transform()->invert_compose(TransformState::make_identity()); + } + + nassertr(verify_complete(), TransformState::make_identity()); + nassertr(other.verify_complete(), TransformState::make_identity()); + + int a_count, b_count; + find_common_ancestor(*this, other, a_count, b_count); + + CPT(TransformState) a_transform = r_get_partial_transform(_head, a_count); + CPT(TransformState) b_transform = r_get_partial_transform(other._head, b_count); + return a_transform->invert_compose(b_transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::verify_complete +// Access: Published +// Description: Returns true if all of the nodes described in the +// NodeChain are connected *and* the top node is the top +// of the graph, or false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodeChain:: +verify_complete() const { + if (is_empty()) { + return true; + } + + uncollapse_head(); + const NodeChainComponent *comp = _head; + nassertr(comp != (const NodeChainComponent *)NULL, false); + + PandaNode *node = comp->get_node(); + nassertr(node != (const PandaNode *)NULL, false); + int length = comp->get_length(); + + comp = comp->get_next(); + length--; + while (comp != (const NodeChainComponent *)NULL) { + PandaNode *next_node = comp->get_node(); + nassertr(next_node != (const PandaNode *)NULL, false); + + if (next_node->find_child(node) < 0) { + return false; + } + + if (comp->get_length() != length) { + return false; + } + + node = next_node; + comp = comp->get_next(); + length--; + } + + // Now that we've reached the top, we should have no parents. + return length == 0 && node->get_num_parents() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::uncollapse_head +// Access: Private +// Description: Quietly and transparently uncollapses the _head +// pointer if it needs it. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +uncollapse_head() const { + if (_head != (NodeChainComponent *)NULL && _head->is_collapsed()) { + ((NodeChain *)this)->_head = _head->uncollapse(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::find_common_ancestor +// Access: Private, Static +// Description: Walks up from both NodeChains to find the first node +// that both have in common, if any. Fills a_count and +// b_count with the number of nodes below the common +// node in each chain. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +find_common_ancestor(const NodeChain &a, const NodeChain &b, + int &a_count, int &b_count) { + nassertv(!a.is_empty() && !b.is_empty()); + a.uncollapse_head(); + b.uncollapse_head(); + + NodeChainComponent *ac = a._head; + NodeChainComponent *bc = b._head; + a_count = 0; + b_count = 0; + + // Shorten up the longer one until they are the same length. + while (ac->get_length() > bc->get_length()) { + nassertv(ac != (NodeChainComponent *)NULL); + ac = ac->get_next(); + a_count++; + } + while (bc->get_length() > ac->get_length()) { + nassertv(bc != (NodeChainComponent *)NULL); + bc = bc->get_next(); + b_count++; + } + + // Now shorten them both up until we reach the same component. + while (ac != bc) { + // These shouldn't go to NULL unless they both go there together. + nassertv(ac != (NodeChainComponent *)NULL); + nassertv(bc != (NodeChainComponent *)NULL); + ac = ac->get_next(); + a_count++; + bc = bc->get_next(); + b_count++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_get_net_state +// Access: Private +// Description: Recursively determines the net state chnages to the +// indicated component node from the root of the graph. +//////////////////////////////////////////////////////////////////// +CPT(RenderState) NodeChain:: +r_get_net_state(NodeChainComponent *comp) const { + if (comp == (NodeChainComponent *)NULL) { + return RenderState::make_empty(); + } else { + CPT(RenderState) state = comp->get_node()->get_state(); + return r_get_net_state(comp->get_next())->compose(state); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_get_partial_state +// Access: Private +// Description: Recursively determines the net state changes to the +// indicated component node from the nth node above it. +//////////////////////////////////////////////////////////////////// +CPT(RenderState) NodeChain:: +r_get_partial_state(NodeChainComponent *comp, int n) const { + nassertr(comp != (NodeChainComponent *)NULL, RenderState::make_empty()); + if (n == 0) { + return RenderState::make_empty(); + } else { + CPT(RenderState) state = comp->get_node()->get_state(); + return r_get_partial_state(comp->get_next(), n - 1)->compose(state); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_get_net_transform +// Access: Private +// Description: Recursively determines the net transform to the +// indicated component node from the root of the graph. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) NodeChain:: +r_get_net_transform(NodeChainComponent *comp) const { + if (comp == (NodeChainComponent *)NULL) { + return TransformState::make_identity(); + } else { + CPT(TransformState) transform = comp->get_node()->get_transform(); + return r_get_net_transform(comp->get_next())->compose(transform); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_get_partial_transform +// Access: Private +// Description: Recursively determines the net transform to the +// indicated component node from the nth node above it. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) NodeChain:: +r_get_partial_transform(NodeChainComponent *comp, int n) const { + nassertr(comp != (NodeChainComponent *)NULL, TransformState::make_identity()); + if (n == 0) { + return TransformState::make_identity(); + } else { + CPT(TransformState) transform = comp->get_node()->get_transform(); + return r_get_partial_transform(comp->get_next(), n - 1)->compose(transform); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_output +// Access: Private +// Description: The recursive implementation of output(), this writes +// the names of each node component in order from +// beginning to end, by first walking to the end of the +// linked list and then outputting from there. +//////////////////////////////////////////////////////////////////// +void NodeChain:: +r_output(ostream &out, NodeChainComponent *comp) const { + NodeChainComponent *next = comp->get_next(); + if (next != (NodeChainComponent *)NULL) { + // This is not the head of the list; keep going up. + r_output(out, next); + out << "/"; + } + + // Now output this component. + PandaNode *node = comp->get_node(); + if (node->has_name()) { + out << node->get_name(); + } else { + out << "+" << node->get_type(); + } + out << "[" << comp->get_length() << "]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChain::r_compare_to +// Access: Private, Static +// Description: The recursive implementation of compare_to(). Returns +// < 0 if a sorts before b, > 0 if b sorts before a, or +// == 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +int NodeChain:: +r_compare_to(const NodeChainComponent *a, const NodeChainComponent *b) { + if (a == b) { + return 0; + + } else if (a == (const NodeChainComponent *)NULL) { + return -1; + + } else if (b == (const NodeChainComponent *)NULL) { + return 1; + + } else if (a->get_node() != b->get_node()) { + return a->get_node() - b->get_node(); + + } else { + return r_compare_to(a->get_next(), b->get_next()); + } +} diff --git a/panda/src/pgraph/nodeChain.h b/panda/src/pgraph/nodeChain.h new file mode 100644 index 0000000000..170ecad89e --- /dev/null +++ b/panda/src/pgraph/nodeChain.h @@ -0,0 +1,176 @@ +// Filename: nodeChain.h +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODECHAIN_H +#define NODECHAIN_H + +#include "pandabase.h" + +#include "pandaNode.h" +#include "renderState.h" +#include "transformState.h" +#include "nodeChainComponent.h" + +#include "pointerTo.h" +#include "referenceCount.h" +#include "notify.h" +#include "typedObject.h" + +//////////////////////////////////////////////////////////////////// +// Class : NodeChain +// Description : A NodeChain is the fundamental system for +// disambiguating instances and manipulating the scene +// graph. NodeChain is the base class of NodePath, +// which adds a bit more high-level functionality, but +// the fundamental scene graph manipulations are defined +// at the NodeChain level. +// +// A NodeChain is a list of connected nodes from the +// root of the graph to any sub-node. Each NodeChain +// therefore unqiuely describes one instance of a node. +// +// NodeChains themselves are lightweight objects that +// may easily be copied and passed by value. Their data +// is stored as a series of NodeChainComponents that are +// stored on the nodes. Holding a NodeChain will keep a +// reference count to all the nodes in the chain. +// However, if any node in the chain is removed or +// reparented (perhaps through a different NodeChain), +// the NodeChain will automatically be updated to +// reflect the changes. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeChain { +PUBLISHED: + // This enumeration is returned by get_error_type() for an empty + // NodeChain to report the reason it's empty. + enum ErrorType { + ET_ok = 0, // i.e. not empty, or never assigned to anything. + ET_not_found, // returned from a failed find() or similar function. + ET_removed, // remove_node() was previously called on this NodeChain. + ET_fail, // general failure return from some function. + }; + + INLINE NodeChain(); + INLINE NodeChain(PandaNode *top_node); + INLINE NodeChain(const NodeChain ©); + INLINE void operator = (const NodeChain ©); + + INLINE static NodeChain not_found(); + INLINE static NodeChain removed(); + INLINE static NodeChain fail(); + + // Methods to query a NodeChain's contents. + INLINE bool is_empty() const; + INLINE bool is_singleton() const; + int get_num_nodes() const; + PandaNode *get_node(int index) const; + + INLINE ErrorType get_error_type() const; + + PandaNode *get_top_node() const; + INLINE PandaNode *node() const; + + // Methods that return collections of NodePaths derived from or + // related to this one. + + /* + NodeChainCollection get_siblings() const; + NodeChainCollection get_children() const; + INLINE int get_num_children() const; + INLINE NodeChain get_child(int n) const; + */ + + INLINE bool has_parent() const; + INLINE NodeChain get_parent() const; + + /* + INLINE NodeChain find_path_down_to(Node *dnode) const; + INLINE NodeChain find(const string &chain) const; + + NodeChainCollection + find_all_paths_down_to(Node *dnode) const; + + NodeChainCollection + find_all_matches(const string &chain) const; + */ + + // Methods that actually move nodes around in the scene graph. The + // optional "sort" parameter can be used to force a particular + // ordering between sibling nodes, useful when dealing with LOD's + // and similar switch nodes. If the sort value is the same, nodes + // will be arranged in the order they were added. + void reparent_to(const NodeChain &other, int sort = 0); + void wrt_reparent_to(const NodeChain &other, int sort = 0); + NodeChain instance_to(const NodeChain &other, int sort = 0) const; + NodeChain copy_to(const NodeChain &other, int sort = 0) const; + NodeChain attach_new_node(PandaNode *node, int sort = 0) const; + INLINE NodeChain attach_new_node(const string &name, int sort = 0) const; + void remove_node(); + + + // Relative transform and state changes between nodes. + CPT(RenderState) get_rel_state(const NodeChain &other) const; + INLINE CPT(RenderState) get_net_state() const; + + CPT(TransformState) get_rel_transform(const NodeChain &other) const; + INLINE CPT(TransformState) get_net_transform() const; + + INLINE const LMatrix4f &get_mat(const NodeChain &other) const; + + bool verify_complete() const; + +public: + INLINE bool operator == (const NodeChain &other) const; + INLINE bool operator != (const NodeChain &other) const; + INLINE bool operator < (const NodeChain &other) const; + INLINE int compare_to(const NodeChain &other) const; + + INLINE void output(ostream &out) const; + +private: + void uncollapse_head() const; + static void find_common_ancestor(const NodeChain &a, const NodeChain &b, + int &a_count, int &b_count); + + CPT(RenderState) r_get_net_state(NodeChainComponent *comp) const; + CPT(RenderState) r_get_partial_state(NodeChainComponent *comp, int n) const; + CPT(TransformState) r_get_net_transform(NodeChainComponent *comp) const; + CPT(TransformState) r_get_partial_transform(NodeChainComponent *comp, int n) const; + void r_output(ostream &out, NodeChainComponent *comp) const; + static int r_compare_to(const NodeChainComponent *a, const NodeChainComponent *v); + + PT(NodeChainComponent) _head; + ErrorType _error_type; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "NodeChain"); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const NodeChain &node_chain); + +#include "nodeChain.I" + +#endif diff --git a/panda/src/pgraph/nodeChainComponent.I b/panda/src/pgraph/nodeChainComponent.I new file mode 100644 index 0000000000..bd5efda076 --- /dev/null +++ b/panda/src/pgraph/nodeChainComponent.I @@ -0,0 +1,203 @@ +// Filename: nodeChainComponent.I +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::CData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent::CData:: +CData() { + _length = 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::CData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent::CData:: +CData(const NodeChainComponent::CData ©) : + _next(copy._next), + _length(copy._length) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::Constructor +// Access: Private +// Description: Constructs a new NodeChainComponent from the +// indicated node. Don't try to call this directly; ask +// the PandaNode to do it for you. +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent:: +NodeChainComponent(PandaNode *node, NodeChainComponent *next) : + _node(node) +{ +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, get_class_type()); +#endif + CDWriter cdata(_cycler); + cdata->_next = next; + + if (next != (NodeChainComponent *)NULL) { + cdata->_length = next->get_length() + 1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::Copy Constructor +// Access: Private +// Description: NodeChainComponents should not be copied. +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent:: +NodeChainComponent(const NodeChainComponent ©) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::Copy Assignment Operator +// Access: Private +// Description: NodeChainComponents should not be copied. +//////////////////////////////////////////////////////////////////// +INLINE void NodeChainComponent:: +operator = (const NodeChainComponent ©) { + nassertv(false); +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent:: +~NodeChainComponent() { + nassertv(_node != (PandaNode *)NULL); + _node->delete_component(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::get_node +// Access: Public +// Description: Returns the node referenced by this component. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *NodeChainComponent:: +get_node() const { + return _node; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::is_top_node +// Access: Public +// Description: Returns true if this component represents the top +// node in the chain. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChainComponent:: +is_top_node() const { + CDReader cdata(_cycler); + return (cdata->_next == (NodeChainComponent *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::is_collapsed +// Access: Public +// Description: Returns true if this component has been collapsed +// with another component. In this case, the component +// itself is invalid, and the collapsed component should +// be used instead. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeChainComponent:: +is_collapsed() const { + CDReader cdata(_cycler); + return (cdata->_length == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::get_length +// Access: Public +// Description: Returns the length of the chain. +//////////////////////////////////////////////////////////////////// +INLINE int NodeChainComponent:: +get_length() const { + CDReader cdata(_cycler); + nassertr(!is_collapsed(), 0); + return cdata->_length; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::get_collapsed +// Access: Public +// Description: If is_collapsed() returns true, this is the component +// that this one has been collapsed with, and should be +// replaced with. +//////////////////////////////////////////////////////////////////// +INLINE NodeChainComponent *NodeChainComponent:: +get_collapsed() const { + CDReader cdata(_cycler); + nassertr(is_collapsed(), (NodeChainComponent *)NULL); + return cdata->_next; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::set_next +// Access: Private +// Description: Sets the next pointer in the chain. +//////////////////////////////////////////////////////////////////// +INLINE void NodeChainComponent:: +set_next(NodeChainComponent *next) { + CDWriter cdata(_cycler); + nassertv(!is_collapsed()); + nassertv(next != (NodeChainComponent *)NULL); + cdata->_next = next; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::set_top_node +// Access: Private +// Description: Severs any connection to the next pointer in the +// chain and makes this component a top node. +//////////////////////////////////////////////////////////////////// +INLINE void NodeChainComponent:: +set_top_node() { + CDWriter cdata(_cycler); + nassertv(!is_collapsed()); + cdata->_next = (NodeChainComponent *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::collapse_with +// Access: Private +// Description: Indicates that this component pointer is no longer +// valid, and that the indicated component should be +// used instead. This is done whenever two +// NodeChainComponents have been collapsed together due +// to an instance being removed higher up in the graph. +//////////////////////////////////////////////////////////////////// +INLINE void NodeChainComponent:: +collapse_with(NodeChainComponent *next) { + CDWriter cdata(_cycler); + nassertv(!is_collapsed()); + nassertv(next != (NodeChainComponent *)NULL); + cdata->_next = next; + cdata->_length = 0; + + // We indicate a component has been collapsed by setting its length + // to zero. +} diff --git a/panda/src/pgraph/nodeChainComponent.cxx b/panda/src/pgraph/nodeChainComponent.cxx new file mode 100644 index 0000000000..dbb2cf59f4 --- /dev/null +++ b/panda/src/pgraph/nodeChainComponent.cxx @@ -0,0 +1,102 @@ +// Filename: nodeChainComponent.cxx +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "nodeChainComponent.h" + +TypeHandle NodeChainComponent::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::CData::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CycleData *NodeChainComponent::CData:: +make_copy() const { + return new CData(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::get_next +// Access: Public +// Description: Returns the next component in the chain. +//////////////////////////////////////////////////////////////////// +NodeChainComponent *NodeChainComponent:: +get_next() const { + CDReader cdata(_cycler); + nassertr(!is_collapsed(), (NodeChainComponent *)NULL); + + NodeChainComponent *next = cdata->_next; + + // If the next component has been collapsed, transparently update + // the pointer to get the actual node, and store the new pointer, + // before we return. Collapsing can happen at any time to any + // component in the chain and we have to deal with it. + if (next != (NodeChainComponent *)NULL && next->is_collapsed()) { + next = next->uncollapse(); + ((NodeChainComponent *)this)->set_next(next); + } + + return next; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::fix_length +// Access: Public +// Description: Checks that the length indicated by the component is +// one more than the length of its predecessor. If this +// is broken, fixes it and returns true indicating the +// component has been changed; otherwise, returns false. +//////////////////////////////////////////////////////////////////// +bool NodeChainComponent:: +fix_length() { + int length_should_be = 1; + if (!is_top_node()) { + length_should_be = get_next()->get_length() + 1; + } + if (get_length() == length_should_be) { + return false; + } + + CDWriter cdata(_cycler); + cdata->_length = length_should_be; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeChainComponent::uncollapse +// Access: Public +// Description: Returns this component pointer if the component is +// not collapsed; or if it has been collapsed, returns +// the pointer it has been collapsed into. +// +// Collapsing can happen at any time to any component in +// the chain and we have to deal with it. It happens +// when a node is removed further up the chain that +// results in two instances becoming the same thing. +//////////////////////////////////////////////////////////////////// +NodeChainComponent *NodeChainComponent:: +uncollapse() { + NodeChainComponent *comp = this; + + while (comp->is_collapsed()) { + comp = comp->get_collapsed(); + } + + return comp; +} diff --git a/panda/src/pgraph/nodeChainComponent.h b/panda/src/pgraph/nodeChainComponent.h new file mode 100644 index 0000000000..a245ba340e --- /dev/null +++ b/panda/src/pgraph/nodeChainComponent.h @@ -0,0 +1,106 @@ +// Filename: nodeChainComponent.h +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODECHAINCOMPONENT_H +#define NODECHAINCOMPONENT_H + +#include "pandabase.h" + +#include "pandaNode.h" +#include "pointerTo.h" +#include "referenceCount.h" +#include "cycleData.h" +#include "cycleDataReader.h" +#include "cycleDataWriter.h" + +//////////////////////////////////////////////////////////////////// +// Class : NodeChainComponent +// Description : This is one component of a NodeChain. These are +// stored on each PandaNode, as many as one for each of +// the possible instances of the node (but they only +// exist when they are requested, to minimize memory +// waste). A NodeChain represents a singly-linked list +// of these from an arbitrary component in the graph to +// the root. +// +// This whole NodeChain system is used to disambiguate +// instances in the scene graph, and the +// NodeChainComponents are stored in the nodes +// themselves to allow the nodes to keep these up to +// date as the scene graph is manipulated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeChainComponent : public ReferenceCount { +private: + INLINE NodeChainComponent(PandaNode *node, NodeChainComponent *next = NULL); + INLINE NodeChainComponent(const NodeChainComponent ©); + INLINE void operator = (const NodeChainComponent ©); + +public: + INLINE ~NodeChainComponent(); + + INLINE PandaNode *get_node() const; + INLINE bool is_top_node() const; + INLINE bool is_collapsed() const; + + NodeChainComponent *get_next() const; + INLINE int get_length() const; + INLINE NodeChainComponent *get_collapsed() const; + + bool fix_length(); + NodeChainComponent *uncollapse(); + +private: + INLINE void set_next(NodeChainComponent *next); + INLINE void set_top_node(); + INLINE void collapse_with(NodeChainComponent *next); + + PT(PandaNode) _node; + + // This is the data that must be cycled between pipeline stages. + class EXPCL_PANDA CData : public CycleData { + public: + INLINE CData(); + CData(const CData ©); + virtual CycleData *make_copy() const; + + PT(NodeChainComponent) _next; + int _length; + }; + + PipelineCycler _cycler; + typedef CycleDataReader CDReader; + typedef CycleDataWriter CDWriter; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + register_type(_type_handle, "NodeChainComponent", + ReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class PandaNode; +}; + +#include "nodeChainComponent.I" + +#endif diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index c58b64c004..b4dd9173d1 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -62,6 +62,42 @@ get_sort() const { return _sort; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::UpConnection::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PandaNode::UpConnection:: +UpConnection(PandaNode *parent) : + _parent(parent) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::UpConnection::operator < +// Access: Public +// Description: Sorts the up connections of a node by pointer. This +// is different from the down connections of a node, +// which are sorted by the specified _sort number. This +// makes it easy to locate a particular parent of a node +// by pointer, or to test for a parent-child +// relationship given two node pointers. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaNode::UpConnection:: +operator < (const UpConnection &other) const { + return _parent < other._parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::UpConnection::get_parent +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *PandaNode::UpConnection:: +get_parent() const { + return _parent; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::CData::Constructor // Access: Public @@ -69,7 +105,8 @@ get_sort() const { //////////////////////////////////////////////////////////////////// INLINE PandaNode::CData:: CData() { - _state_changes = RenderState::make_empty(); + _state = RenderState::make_empty(); + _transform = TransformState::make_identity(); } //////////////////////////////////////////////////////////////////// @@ -140,7 +177,7 @@ INLINE PandaNode *PandaNode:: get_parent(int n) const { CDReader cdata(_cycler); nassertr(n >= 0 && n < (int)cdata->_up.size(), NULL); - return cdata->_up[n]; + return cdata->_up[n].get_parent(); } //////////////////////////////////////////////////////////////////// @@ -152,7 +189,7 @@ get_parent(int n) const { INLINE int PandaNode:: find_parent(PandaNode *node) const { CDReader cdata(_cycler); - Up::const_iterator ui = cdata->_up.find(node); + Up::const_iterator ui = cdata->_up.find(UpConnection(node)); if (ui == cdata->_up.end()) { return -1; } @@ -212,7 +249,7 @@ get_child_sort(int n) const { INLINE void PandaNode:: set_attrib(const RenderAttrib *attrib, int override) { CDWriter cdata(_cycler); - cdata->_state_changes = cdata->_state_changes->add(attrib, override); + cdata->_state = cdata->_state->add(attrib, override); } //////////////////////////////////////////////////////////////////// @@ -227,9 +264,9 @@ set_attrib(const RenderAttrib *attrib, int override) { INLINE const RenderAttrib *PandaNode:: get_attrib(TypeHandle type) const { CDReader cdata(_cycler); - int index = cdata->_state_changes->find_attrib(type); + int index = cdata->_state->find_attrib(type); if (index >= 0) { - return cdata->_state_changes->get_attrib(index); + return cdata->_state->get_attrib(index); } return NULL; } @@ -245,7 +282,7 @@ get_attrib(TypeHandle type) const { INLINE void PandaNode:: clear_attrib(TypeHandle type) { CDWriter cdata(_cycler); - cdata->_state_changes = cdata->_state_changes->remove(type); + cdata->_state = cdata->_state->remove(type); } //////////////////////////////////////////////////////////////////// @@ -261,7 +298,7 @@ clear_attrib(TypeHandle type) { INLINE void PandaNode:: set_state(const RenderState *state) { CDWriter cdata(_cycler); - cdata->_state_changes = state; + cdata->_state = state; } //////////////////////////////////////////////////////////////////// @@ -276,7 +313,7 @@ set_state(const RenderState *state) { INLINE const RenderState *PandaNode:: get_state() const { CDReader cdata(_cycler); - return cdata->_state_changes; + return cdata->_state; } //////////////////////////////////////////////////////////////////// @@ -290,7 +327,46 @@ get_state() const { INLINE void PandaNode:: clear_state() { CDWriter cdata(_cycler); - cdata->_state_changes = RenderState::make_empty(); + cdata->_state = RenderState::make_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::set_transform +// Access: Published +// Description: Sets the transform that will be applied to this node +// and below. This defines a new coordinate space at +// this point in the scene graph and below. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +set_transform(const TransformState *transform) { + CDWriter cdata(_cycler); + cdata->_transform = transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_transform +// Access: Published +// Description: Returns the transform that has been set on this +// particular node. This is not the net transform from +// the root, but simply the transform on this particular +// node. +//////////////////////////////////////////////////////////////////// +INLINE const TransformState *PandaNode:: +get_transform() const { + CDReader cdata(_cycler); + return cdata->_transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::clear_transform +// Access: Published +// Description: Resets the transform on this node to the identity +// transform. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +clear_transform() { + CDWriter cdata(_cycler); + cdata->_transform = TransformState::make_identity(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 9010cf9c3b..a8dba7e294 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -17,6 +17,8 @@ //////////////////////////////////////////////////////////////////// #include "pandaNode.h" +#include "config_pgraph.h" +#include "nodeChainComponent.h" #include "bamReader.h" #include "bamWriter.h" #include "indent.h" @@ -34,9 +36,11 @@ PandaNode::CData:: CData(const PandaNode::CData ©) : _down(copy._down), _up(copy._up), + _chains(copy._chains), _node_bounds(copy._node_bounds), _subgraph_bounds(copy._subgraph_bounds), - _state_changes(copy._state_changes) + _state(copy._state), + _transform(copy._transform) { } @@ -74,9 +78,11 @@ PandaNode(const PandaNode ©) : { // Copying a node does not copy its children. - // Copy the other node's bounding volume. + // Copy the other node's state and bounding volume. CDReader copy_cdata(copy._cycler); CDWriter cdata(_cycler); + cdata->_state = copy_cdata->_state; + cdata->_transform = copy_cdata->_transform; cdata->_node_bounds = copy_cdata->_node_bounds; } @@ -91,9 +97,11 @@ operator = (const PandaNode ©) { Namable::operator = (copy); ReferenceCount::operator = (copy); - // Copy the other node's bounding volume. + // Copy the other node's state and bounding volume. CDReader copy_cdata(copy._cycler); CDWriter cdata(_cycler); + cdata->_state = copy_cdata->_state; + cdata->_transform = copy_cdata->_transform; cdata->_node_bounds = copy_cdata->_node_bounds; } @@ -144,19 +152,32 @@ find_child(PandaNode *node) const { // // If the same child is added to a node more than once, // the previous instance is first removed. -// -// The return value is the index of the new child. //////////////////////////////////////////////////////////////////// -int PandaNode:: -add_child(PandaNode *child, int sort) { - remove_child(child); +void PandaNode:: +add_child(PandaNode *child_node, int sort) { + // Ensure the child_node is not deleted while we do this. + PT(PandaNode) keep_child = child_node; + remove_child(child_node); CDWriter cdata(_cycler); - CDWriter cdata_child(child->_cycler); + CDWriter cdata_child(child_node->_cycler); + + cdata->_down.insert(DownConnection(child_node, sort)); + cdata_child->_up.insert(UpConnection(this)); - Down::iterator ci = cdata->_down.insert(DownConnection(child, sort)); - cdata_child->_up.insert(this); + // We also have to adjust any NodeChainComponents the child might + // have that reference the child as a top node. Any other + // components we can leave alone, because we are making a new + // instance of the child. + Chains::iterator ci; + for (ci = cdata_child->_chains.begin(); + ci != cdata_child->_chains.end(); + ++ci) { + if ((*ci)->is_top_node()) { + (*ci)->set_next(get_generic_component()); + } + } - return ci - cdata->_down.begin(); + child_node->fix_chain_lengths(); } //////////////////////////////////////////////////////////////////// @@ -169,12 +190,41 @@ remove_child(int n) { CDWriter cdata(_cycler); nassertv(n >= 0 && n < (int)cdata->_down.size()); - PandaNode *child = cdata->_down[n].get_child(); - CDWriter cdata_child(child->_cycler); + PT(PandaNode) child_node = cdata->_down[n].get_child(); + CDWriter cdata_child(child_node->_cycler); cdata->_down.erase(cdata->_down.begin() + n); - int num_erased = cdata_child->_up.erase(this); + int num_erased = cdata_child->_up.erase(UpConnection(this)); nassertv(num_erased == 1); + + // Now sever any NodeChainComponents on the child that reference + // this node. If we have multiple of these, we have to collapse + // them together. + NodeChainComponent *collapsed = (NodeChainComponent *)NULL; + Chains::iterator ci; + ci = cdata_child->_chains.begin(); + while (ci != cdata_child->_chains.end()) { + Chains::iterator cnext = ci; + ++cnext; + if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) { + if (collapsed == (NodeChainComponent *)NULL) { + (*ci)->set_top_node(); + collapsed = (*ci); + } else { + // This is a different component that used to reference a + // different instance, but now it's all just the same topnode. + // We have to collapse this and the previous one together. + // However, there might be some NodeChains out there that + // still keep a pointer to this one, so we can't remove it + // altogether. + (*ci)->collapse_with(collapsed); + cdata_child->_chains.erase(ci); + } + } + ci = cnext; + } + + child_node->fix_chain_lengths(); } //////////////////////////////////////////////////////////////////// @@ -185,24 +235,49 @@ remove_child(int n) { // already a child of the node. //////////////////////////////////////////////////////////////////// bool PandaNode:: -remove_child(PandaNode *child) { - CDWriter cdata_child(child->_cycler); +remove_child(PandaNode *child_node) { + // Ensure the child_node is not deleted while we do this. + PT(PandaNode) keep_child = child_node; + CDWriter cdata_child(child_node->_cycler); // First, look for and remove this node from the child's parent // list. - int num_erased = cdata_child->_up.erase(this); + int num_erased = cdata_child->_up.erase(UpConnection(this)); if (num_erased == 0) { // No such node; it wasn't our child to begin with. return false; } + // Now sever any NodeChainComponents on the child that reference + // this node. If we have multiple of these, we have to collapse + // them together (see above). + NodeChainComponent *collapsed = (NodeChainComponent *)NULL; + Chains::iterator ci; + ci = cdata_child->_chains.begin(); + while (ci != cdata_child->_chains.end()) { + Chains::iterator cnext = ci; + ++cnext; + if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) { + if (collapsed == (NodeChainComponent *)NULL) { + (*ci)->set_top_node(); + collapsed = (*ci); + } else { + (*ci)->collapse_with(collapsed); + cdata_child->_chains.erase(ci); + } + } + ci = cnext; + } + + child_node->fix_chain_lengths(); + CDWriter cdata(_cycler); // Now, look for and remove the child node from our down list. - Down::iterator ci; - for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) { - if ((*ci).get_child() == child) { - cdata->_down.erase(ci); + Down::iterator di; + for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) { + if ((*di).get_child() == child_node) { + cdata->_down.erase(di); return true; } } @@ -222,9 +297,32 @@ remove_all_children() { CDWriter cdata(_cycler); Down::iterator ci; for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) { - PandaNode *child = (*ci).get_child(); - CDWriter child_cdata(child->_cycler); - child_cdata->_up.erase(this); + PT(PandaNode) child_node = (*ci).get_child(); + CDWriter cdata_child(child_node->_cycler); + cdata_child->_up.erase(UpConnection(this)); + + // Now sever any NodeChainComponents on the child that reference + // this node. If we have multiple of these, we have to collapse + // them together (see above). + NodeChainComponent *collapsed = (NodeChainComponent *)NULL; + Chains::iterator ci; + ci = cdata_child->_chains.begin(); + while (ci != cdata_child->_chains.end()) { + Chains::iterator cnext = ci; + ++cnext; + if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) { + if (collapsed == (NodeChainComponent *)NULL) { + (*ci)->set_top_node(); + collapsed = (*ci); + } else { + (*ci)->collapse_with(collapsed); + cdata_child->_chains.erase(ci); + } + } + ci = cnext; + } + + child_node->fix_chain_lengths(); } } @@ -247,8 +345,11 @@ void PandaNode:: write(ostream &out, int indent_level) const { indent(out, indent_level) << *this; CDReader cdata(_cycler); - if (!cdata->_state_changes->is_empty()) { - out << " (" << *cdata->_state_changes << ")"; + if (!cdata->_transform->is_identity()) { + out << " T"; + } + if (!cdata->_state->is_empty()) { + out << " (" << *cdata->_state << ")"; } out << "\n"; } @@ -269,6 +370,286 @@ is_geom_node() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::attach +// Access: Private, Static +// Description: Creates a new parent-child relationship, and returns +// the new NodeChainComponent. If the child was already +// attached to the indicated parent, repositions it and +// returns the original NodeChainComponent. +//////////////////////////////////////////////////////////////////// +PT(NodeChainComponent) PandaNode:: +attach(NodeChainComponent *parent, PandaNode *child_node, int sort) { + nassertr(parent != (NodeChainComponent *)NULL, (NodeChainComponent *)NULL); + + // See if the child was already attached to the parent. If it was, + // we'll use that same NodeChainComponent. + PT(NodeChainComponent) child = get_component(parent, child_node); + + if (child == (NodeChainComponent *)NULL) { + // The child was not already attached to the parent, so get a new + // component. + child = get_top_component(child_node); + } + + reparent(parent, child, sort); + return child; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::detach +// Access: Private, Static +// Description: Breaks a parent-child relationship. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +detach(NodeChainComponent *child) { + nassertv(child != (NodeChainComponent *)NULL); + nassertv(!child->is_top_node()); + PandaNode *child_node = child->get_node(); + PandaNode *parent_node = child->get_next()->get_node(); + + // Break the NodeChainComponent connection. + child->set_top_node(); + + CDWriter cdata_child(child_node->_cycler); + + // Any other components in the same child_node that previously + // referenced the same parent has now become invalid and must be + // collapsed into this one and removed from the chains set. + Chains::iterator ci; + ci = cdata_child->_chains.begin(); + while (ci != cdata_child->_chains.end()) { + Chains::iterator cnext = ci; + ++cnext; + if ((*ci) != child && !(*ci)->is_top_node() && + (*ci)->get_next()->get_node() == parent_node) { + (*ci)->collapse_with(child); + cdata_child->_chains.erase(ci); + } + ci = cnext; + } + + child_node->fix_chain_lengths(); + + // Now look for the child and break the actual connection. + + // First, look for and remove the parent node from the child's up + // list. + int num_erased = cdata_child->_up.erase(UpConnection(parent_node)); + nassertv(num_erased == 1); + + CDWriter cdata_parent(parent_node->_cycler); + + // Now, look for and remove the child node from the parent's down list. + Down::iterator di; + for (di = cdata_parent->_down.begin(); + di != cdata_parent->_down.end(); + ++di) { + if ((*di).get_child() == child_node) { + cdata_parent->_down.erase(di); + return; + } + } + + // We shouldn't get here unless there was a parent-child mismatch. + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::reparent +// Access: Private, Static +// Description: Switches a node from one parent to another. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +reparent(NodeChainComponent *new_parent, NodeChainComponent *child, int sort) { + nassertv(new_parent != (NodeChainComponent *)NULL); + nassertv(child != (NodeChainComponent *)NULL); + + if (!child->is_top_node()) { + detach(child); + } + + // Adjust the NodeChainComponents. + child->set_next(new_parent); + + PandaNode *child_node = child->get_node(); + PandaNode *parent_node = new_parent->get_node(); + + // Now reattach at the indicated sort position. + CDWriter cdata_parent(parent_node->_cycler); + CDWriter cdata_child(child_node->_cycler); + + cdata_parent->_down.insert(DownConnection(child_node, sort)); + cdata_child->_up.insert(UpConnection(parent_node)); + + cdata_child->_chains.insert(child); + child_node->fix_chain_lengths(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_component +// Access: Private, Static +// Description: Returns the NodeChainComponent based on the indicated +// child of the given parent, or NULL if there is no +// such parent-child relationship. +//////////////////////////////////////////////////////////////////// +PT(NodeChainComponent) PandaNode:: +get_component(NodeChainComponent *parent, PandaNode *child_node) { + nassertr(parent != (NodeChainComponent *)NULL, (NodeChainComponent *)NULL); + PandaNode *parent_node = parent->get_node(); + + { + CDReader cdata_child(child_node->_cycler); + + // First, walk through the list of NodeChainComponents we already + // have on the child, looking for one that already exists, + // referencing the indicated parent component. + Chains::iterator ci; + for (ci = cdata_child->_chains.begin(); + ci != cdata_child->_chains.end(); + ++ci) { + if ((*ci)->get_next() == parent) { + // If we already have such a component, just return it. + return (*ci); + } + } + } + + // We don't already have a NodeChainComponent referring to this + // parent-child relationship. Are they actually related? + int child_index = child_node->find_parent(parent_node); + if (child_index >= 0) { + // They are. Create and return a new one. + PT(NodeChainComponent) child = + new NodeChainComponent(child_node, parent); + CDWriter cdata_child(child_node->_cycler); + cdata_child->_chains.insert(child); + return child; + } else { + // They aren't related. Return NULL. + return NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_top_component +// Access: Private, Static +// Description: Returns a NodeChainComponent referencing the +// indicated node as a singleton. It is invalid to call +// this for a node that has parents, unless you are +// about to create a new instance (and immediately +// reconnect the NodeChainComponent elsewhere). +//////////////////////////////////////////////////////////////////// +PT(NodeChainComponent) PandaNode:: +get_top_component(PandaNode *child_node) { + { + CDReader cdata_child(child_node->_cycler); + + // Walk through the list of NodeChainComponents we already have on + // the child, looking for one that already exists as a top node. + Chains::const_iterator ci; + for (ci = cdata_child->_chains.begin(); + ci != cdata_child->_chains.end(); + ++ci) { + if ((*ci)->is_top_node()) { + // If we already have such a component, just return it. + return (*ci); + } + } + } + + // We don't already have such a NodeChainComponent; create and + // return a new one. + PT(NodeChainComponent) child = + new NodeChainComponent(child_node, (NodeChainComponent *)NULL); + CDWriter cdata_child(child_node->_cycler); + cdata_child->_chains.insert(child); + + return child; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_generic_component +// Access: Private +// Description: Returns a NodeChainComponent referencing this node as +// a chain from the root. It is only valid to call this +// if there is an unambiguous path from the root; +// otherwise, a warning will be issued and one path will +// be chosen arbitrarily. +//////////////////////////////////////////////////////////////////// +PT(NodeChainComponent) PandaNode:: +get_generic_component() { + int num_parents = get_num_parents(); + if (num_parents == 0) { + return get_top_component(this); + + } else { + if (num_parents != 1) { + pgraph_cat.warning() + << *this << " has " << num_parents + << " parents; choosing arbitrary path to root.\n"; + } + PT(NodeChainComponent) parent = get_parent(0)->get_generic_component(); + return get_component(parent, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::delete_component +// Access: Private +// Description: Removes a NodeChainComponent from the set prior to +// its deletion. This should only be called by the +// NodeChainComponent destructor. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +delete_component(NodeChainComponent *component) { + // We have to remove the component from all of the pipeline stages, + // not just the current one. + int max_num_erased = 0; + + int num_stages = _cycler.get_num_stages(); + for (int i = 0; i < num_stages; i++) { + if (_cycler.is_stage_unique(i)) { + CData *cdata = _cycler.write_stage(i); + int num_erased = cdata->_chains.erase(component); + max_num_erased = max(max_num_erased, num_erased); + _cycler.release_write_stage(i, cdata); + } + } + nassertv(max_num_erased == 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::fix_chain_lengths +// Access: Private +// Description: Recursively fixes the _length member of each +// NodeChainComponent at this level and below, after an +// add or delete child operation that might have messed +// these up. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +fix_chain_lengths() { + CDReader cdata(_cycler); + bool any_wrong = false; + + Chains::const_iterator ci; + for (ci = cdata->_chains.begin(); ci != cdata->_chains.end(); ++ci) { + if ((*ci)->fix_length()) { + any_wrong = true; + } + } + + // If any chains were updated, we have to recurse on all of our + // children, since any one of those chains might be shared by any of + // our child nodes. + if (any_wrong) { + Down::const_iterator di; + for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) { + (*di).get_child()->fix_chain_lengths(); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 9d18a06397..14e40eab24 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -26,6 +26,7 @@ #include "cycleDataWriter.h" #include "pipelineCycler.h" #include "renderState.h" +#include "transformState.h" #include "typedWritable.h" #include "boundedObject.h" @@ -35,6 +36,8 @@ #include "ordered_vector.h" #include "pointerTo.h" +class NodeChainComponent; + //////////////////////////////////////////////////////////////////// // Class : PandaNode // Description : A basic node of the scene graph or data graph. This @@ -58,9 +61,9 @@ PUBLISHED: INLINE int get_child_sort(int n) const; int find_child(PandaNode *node) const; - int add_child(PandaNode *child, int sort = 0); + void add_child(PandaNode *child_node, int sort = 0); void remove_child(int n); - bool remove_child(PandaNode *child); + bool remove_child(PandaNode *child_node); /* bool stash_child(PandaNode *child); @@ -80,12 +83,31 @@ PUBLISHED: INLINE const RenderState *get_state() const; INLINE void clear_state(); + INLINE void set_transform(const TransformState *transform); + INLINE const TransformState *get_transform() const; + INLINE void clear_transform(); + virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; public: virtual bool is_geom_node() const; +private: + // parent-child manipulation for NodeChain support. Don't try to + // call these directly. + static PT(NodeChainComponent) attach(NodeChainComponent *parent, + PandaNode *child, int sort); + static void detach(NodeChainComponent *child); + static void reparent(NodeChainComponent *new_parent, + NodeChainComponent *child, int sort); + static PT(NodeChainComponent) get_component(NodeChainComponent *parent, + PandaNode *child); + static PT(NodeChainComponent) get_top_component(PandaNode *child); + PT(NodeChainComponent) get_generic_component(); + void delete_component(NodeChainComponent *component); + void fix_chain_lengths(); + private: class EXPCL_PANDA DownConnection { public: @@ -102,13 +124,27 @@ private: int _sort; }; typedef ov_multiset Down; - // Parent pointers are not reference counted. That way, parents and - // children do not circularly reference each other. In fact, parent - // pointers are just simple pointers, with no additional data. We - // don't really need to keep the parent pointers around, but it's - // nice to be able to walk up the graph. - typedef ov_set Up; + class EXPCL_PANDA 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 Up; + + // We also maintain a set of NodeChainComponents in the node. This + // represents the set of instances of this node that we have + // requested a NodeChain for. We don't keep reference counts; when + // each NodeChainComponent destructs, it removes itself from this + // set. + typedef pset Chains; + // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { public: @@ -118,10 +154,12 @@ private: Down _down; Up _up; + Chains _chains; BoundedObject _node_bounds; BoundedObject _subgraph_bounds; - CPT(RenderState) _state_changes; + CPT(RenderState) _state; + CPT(TransformState) _transform; }; PipelineCycler _cycler; @@ -175,6 +213,8 @@ private: static TypeHandle _type_handle; friend class PandaNode::Children; + friend class NodeChain; + friend class NodeChainComponent; }; INLINE ostream &operator << (ostream &out, const PandaNode &node) { diff --git a/panda/src/pgraph/pgraph_composite1.cxx b/panda/src/pgraph/pgraph_composite1.cxx index be9362eb84..9a778f6f8a 100644 --- a/panda/src/pgraph/pgraph_composite1.cxx +++ b/panda/src/pgraph/pgraph_composite1.cxx @@ -6,4 +6,6 @@ #include "cycleDataReader.cxx" #include "cycleDataWriter.cxx" #include "qpgeomNode.cxx" -#include "pandaNode.cxx" +#include "qplensNode.cxx" +#include "nodeChain.cxx" +#include "nodeChainComponent.cxx" diff --git a/panda/src/pgraph/pgraph_composite2.cxx b/panda/src/pgraph/pgraph_composite2.cxx index 8f652b82d5..3c8c75d8a7 100644 --- a/panda/src/pgraph/pgraph_composite2.cxx +++ b/panda/src/pgraph/pgraph_composite2.cxx @@ -1,3 +1,4 @@ +#include "pandaNode.cxx" #include "pipeline.cxx" #include "pipelineCycler.cxx" #include "pipelineCyclerBase.cxx" @@ -5,5 +6,4 @@ #include "renderState.cxx" #include "test_pgraph.cxx" #include "textureAttrib.cxx" -#include "transformAttrib.cxx" - +#include "transformState.cxx" diff --git a/panda/src/pgraph/pipelineCycler.I b/panda/src/pgraph/pipelineCycler.I index d8c800498e..daf4c18098 100644 --- a/panda/src/pgraph/pipelineCycler.I +++ b/panda/src/pgraph/pipelineCycler.I @@ -28,3 +28,36 @@ PipelineCycler(Pipeline *pipeline) : PipelineCyclerBase(new CycleDataType, pipeline) { } + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCycler::read +// Access: Public +// Description: See PipelineCyclerBase::read(). +//////////////////////////////////////////////////////////////////// +template +INLINE const CycleDataType *PipelineCycler:: +read() const { + return (const CycleDataType *)PipelineCyclerBase::read(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCycler::write +// Access: Public +// Description: See PipelineCyclerBase::write(). +//////////////////////////////////////////////////////////////////// +template +INLINE CycleDataType *PipelineCycler:: +write() { + return (CycleDataType *)PipelineCyclerBase::write(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCycler::write_stage +// Access: Public +// Description: See PipelineCyclerBase::write_stage(). +//////////////////////////////////////////////////////////////////// +template +INLINE CycleDataType *PipelineCycler:: +write_stage(int n) { + return (CycleDataType *)PipelineCyclerBase::write_stage(n); +} diff --git a/panda/src/pgraph/pipelineCycler.h b/panda/src/pgraph/pipelineCycler.h index 4e2c7c00d1..33a7e025dc 100644 --- a/panda/src/pgraph/pipelineCycler.h +++ b/panda/src/pgraph/pipelineCycler.h @@ -53,6 +53,10 @@ template class PipelineCycler : public PipelineCyclerBase { public: INLINE PipelineCycler(Pipeline *pipeline = NULL); + + INLINE const CycleDataType *read() const; + INLINE CycleDataType *write(); + INLINE CycleDataType *write_stage(int n); }; #include "pipelineCycler.I" diff --git a/panda/src/pgraph/pipelineCyclerBase.I b/panda/src/pgraph/pipelineCyclerBase.I index 6b7f458807..82d25b99c0 100644 --- a/panda/src/pgraph/pipelineCyclerBase.I +++ b/panda/src/pgraph/pipelineCyclerBase.I @@ -107,3 +107,56 @@ release_write(CycleData *pointer) { nassertv(_write_count > 0); _write_count--; } + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerBase::get_num_stages +// Access: Public +// Description: Returns the number of stages in the pipeline. +//////////////////////////////////////////////////////////////////// +INLINE int PipelineCyclerBase:: +get_num_stages() { + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerBase::is_stage_unique +// Access: Public +// Description: Returns true if the nth stage is a different pointer +// than the previous stage, or false if its pointer is +// shared with the previous one. +//////////////////////////////////////////////////////////////////// +INLINE bool PipelineCyclerBase:: +is_stage_unique(int n) const { + nassertr(n == 0, false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerBase::write_stage +// Access: Public +// Description: Returns a pointer suitable for writing to the nth +// stage of the pipeline. This is for special +// applications that need to update the entire pipeline +// at once (for instance, to remove an invalid pointer). +// This pointer should later be released with +// release_write_stage(). +//////////////////////////////////////////////////////////////////// +INLINE CycleData *PipelineCyclerBase:: +write_stage(int n) { + nassertr(n == 0, (CycleData *)NULL); + _stage_count++; + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerBase::release_write_stage +// Access: Public +// Description: Releases a pointer previously obtained via a call to +// write_stage(). +//////////////////////////////////////////////////////////////////// +INLINE void PipelineCyclerBase:: +release_write_stage(int n, CycleData *pointer) { + nassertv(n == 0 && pointer == _data); + nassertv(_stage_count > 0); + _stage_count--; +} diff --git a/panda/src/pgraph/pipelineCyclerBase.cxx b/panda/src/pgraph/pipelineCyclerBase.cxx index 952c929fb4..47ab2f55d3 100644 --- a/panda/src/pgraph/pipelineCyclerBase.cxx +++ b/panda/src/pgraph/pipelineCyclerBase.cxx @@ -29,7 +29,8 @@ PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) : _data(initial_data), _pipeline(pipeline), _read_count(0), - _write_count(0) + _write_count(0), + _stage_count(0) { if (_pipeline == (Pipeline *)NULL) { _pipeline = Pipeline::get_render_pipeline(); @@ -43,6 +44,6 @@ PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) : //////////////////////////////////////////////////////////////////// PipelineCyclerBase:: ~PipelineCyclerBase() { - nassertv(_read_count == 0 && _write_count == 0); + nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0); } diff --git a/panda/src/pgraph/pipelineCyclerBase.h b/panda/src/pgraph/pipelineCyclerBase.h index f84dd77a28..893faf45a9 100644 --- a/panda/src/pgraph/pipelineCyclerBase.h +++ b/panda/src/pgraph/pipelineCyclerBase.h @@ -43,10 +43,15 @@ public: INLINE void increment_write(CycleData *pointer); INLINE void release_write(CycleData *pointer); + INLINE int get_num_stages(); + INLINE bool is_stage_unique(int n) const; + INLINE CycleData *write_stage(int n); + INLINE void release_write_stage(int n, CycleData *pointer); + private: PT(CycleData) _data; Pipeline *_pipeline; - short _read_count, _write_count; + short _read_count, _write_count, _stage_count; }; #include "pipelineCyclerBase.I" diff --git a/panda/src/pgraph/qpcamera.I b/panda/src/pgraph/qpcamera.I new file mode 100644 index 0000000000..0f3fc1d64b --- /dev/null +++ b/panda/src/pgraph/qpcamera.I @@ -0,0 +1,87 @@ +// Filename: qpcamera.I +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::set_active +// Access: Published +// Description: Sets the active flag on the camera. When the camera +// is not active, nothing will be rendered. +//////////////////////////////////////////////////////////////////// +INLINE void qpCamera:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::is_active +// Access: Published +// Description: Returns the current setting of the active flag on the +// camera. +//////////////////////////////////////////////////////////////////// +INLINE bool qpCamera:: +is_active() const { + return _active; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::set_scene +// Access: Published +// Description: Sets the scene that will be rendered by the camera. +// This is normally the root node of a scene graph, +// typically a node called 'render', although it could +// represent the root of any subgraph. +//////////////////////////////////////////////////////////////////// +INLINE void qpCamera:: +set_scene(PandaNode *scene) { + _scene = scene; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::get_scene +// Access: Published +// Description: Returns the scene that will be rendered by the +// camera. See set_scene(). +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *qpCamera:: +get_scene() const { + return _scene; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::get_num_display_regions +// Access: Published +// Description: Returns the number of display regions associated with +// the camera. +//////////////////////////////////////////////////////////////////// +INLINE int qpCamera:: +get_num_display_regions() const { + return _display_regions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::get_display_region +// Access: Published +// Description: Returns the nth display region associated with the +// camera. +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegion *qpCamera:: +get_display_region(int n) const { + nassertr(n >= 0 && n < (int)_display_regions.size(), (DisplayRegion *)NULL); + return _display_regions[n]; +} diff --git a/panda/src/pgraph/qpcamera.cxx b/panda/src/pgraph/qpcamera.cxx new file mode 100644 index 0000000000..97b3f05835 --- /dev/null +++ b/panda/src/pgraph/qpcamera.cxx @@ -0,0 +1,144 @@ +// Filename: qpcamera.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" +#include "qpcamera.h" +#include "lens.h" +#include "throw_event.h" + +TypeHandle qpCamera::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +qpCamera:: +qpCamera(const string &name) : + qpLensNode(name), + _active(true), + _scene((PandaNode *)NULL) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpCamera:: +qpCamera(const qpCamera ©) : + qpLensNode(copy), + _active(copy._active), + _scene(copy._scene) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpCamera:: +operator = (const qpCamera ©) { + qpLensNode::operator = (copy); + _active = copy._active; + _scene = copy._scene; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +qpCamera:: +~qpCamera() { + // We don't have to destroy the display region(s) associated with + // the camera; they're responsible for themselves. However, they + // should have removed themselves before we destruct, or something + // went wrong. + nassertv(_display_regions.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +PandaNode *qpCamera:: +make_copy() const { + return new qpCamera(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::safe_to_flatten +// Access: Public, Virtual +// Description: Returns true if it is generally safe to flatten out +// this particular kind of Node by duplicating +// instances, false otherwise (for instance, a qpCamera +// cannot be safely flattened, because the qpCamera +// pointer itself is meaningful). +//////////////////////////////////////////////////////////////////// +bool qpCamera:: +safe_to_flatten() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::safe_to_transform +// Access: Public, Virtual +// Description: Returns true if it is generally safe to transform +// this particular kind of Node by calling the xform() +// method, false otherwise. For instance, it's usually +// a bad idea to attempt to xform a Character. +//////////////////////////////////////////////////////////////////// +bool qpCamera:: +safe_to_transform() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::add_display_region +// Access: Private +// Description: Adds the indicated DisplayRegion to the set of +// DisplayRegions shared by the camera. This is only +// intended to be called from the DisplayRegion. +//////////////////////////////////////////////////////////////////// +void qpCamera:: +add_display_region(DisplayRegion *display_region) { + _display_regions.push_back(display_region); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCamera::remove_display_region +// Access: Private +// Description: Removes the indicated DisplayRegion from the set of +// DisplayRegions shared by the camera. This is only +// intended to be called from the DisplayRegion. +//////////////////////////////////////////////////////////////////// +void qpCamera:: +remove_display_region(DisplayRegion *display_region) { + DisplayRegions::iterator dri = + find(_display_regions.begin(), _display_regions.end(), display_region); + if (dri != _display_regions.end()) { + _display_regions.erase(dri); + } +} diff --git a/panda/src/pgraph/qpcamera.h b/panda/src/pgraph/qpcamera.h new file mode 100644 index 0000000000..3bff9a3697 --- /dev/null +++ b/panda/src/pgraph/qpcamera.h @@ -0,0 +1,90 @@ +// Filename: qpcamera.h +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef QPCAMERA_H +#define QPCAMERA_H + +#include "pandabase.h" + +#include "qplensNode.h" + +class DisplayRegion; +class PandaNode; + +//////////////////////////////////////////////////////////////////// +// Class : qpCamera +// Description : A node that can be positioned around in the scene +// graph to represent a point of view for rendering a +// scene. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpCamera : public qpLensNode { +PUBLISHED: + qpCamera(const string &name); + +public: + qpCamera(const qpCamera ©); + void operator = (const qpCamera ©); + virtual ~qpCamera(); + + virtual PandaNode *make_copy() const; + virtual bool safe_to_flatten() const; + virtual bool safe_to_transform() const; + +PUBLISHED: + INLINE void set_active(bool active); + INLINE bool is_active() const; + + INLINE void set_scene(PandaNode *scene); + INLINE PandaNode *get_scene() const; + + INLINE int get_num_display_regions() const; + INLINE DisplayRegion *get_display_region(int n) const; + +private: + void add_display_region(DisplayRegion *display_region); + void remove_display_region(DisplayRegion *display_region); + + bool _active; + PandaNode *_scene; + + typedef pvector DisplayRegions; + DisplayRegions _display_regions; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + qpLensNode::init_type(); + register_type(_type_handle, "qpCamera", + qpLensNode::get_class_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; + + friend class DisplayRegion; +}; + +#include "qpcamera.I" + +#endif diff --git a/panda/src/pgraph/qpcullTraverser.cxx b/panda/src/pgraph/qpcullTraverser.cxx index 5ae79738b8..6e679f97d0 100644 --- a/panda/src/pgraph/qpcullTraverser.cxx +++ b/panda/src/pgraph/qpcullTraverser.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "qpcullTraverser.h" +#include "transformState.h" #include "renderState.h" #include "cullHandler.h" #include "dcast.h" @@ -30,7 +31,7 @@ qpCullTraverser:: qpCullTraverser() { _initial_state = RenderState::make_empty(); - _world_transform = DCAST(TransformAttrib, TransformAttrib::make_identity()); + _world_transform = DCAST(TransformState, TransformState::make_identity()); _cull_handler = (CullHandler *)NULL; } @@ -49,17 +50,11 @@ set_initial_state(const RenderState *initial_state) { //////////////////////////////////////////////////////////////////// // Function: qpCullTraverser::set_world_transform // Access: Public -// Description: Specifies the net inverse transform of the camera -// viewing the scene. Normally, this is the same as the -// transform specified in the initial state, but it -// might be different if we are culling a subgraph, -// for instance. -// -// This is used to evaluate camera-dependent attributes -// like billboards and LOD nodes. +// Description: Specifies the position of the world relative to the +// camera. //////////////////////////////////////////////////////////////////// void qpCullTraverser:: -set_world_transform(const TransformAttrib *world_transform) { +set_world_transform(const TransformState *world_transform) { _world_transform = world_transform; } @@ -67,7 +62,7 @@ set_world_transform(const TransformAttrib *world_transform) { // Function: qpCullTraverser::set_cull_handler // Access: Public // Description: Specifies the object that will receive the culled -// Geoms. This must be called before traverse(). +// Geoms. This must be set before calling traverse(). //////////////////////////////////////////////////////////////////// void qpCullTraverser:: set_cull_handler(CullHandler *cull_handler) { @@ -83,7 +78,7 @@ void qpCullTraverser:: traverse(PandaNode *root) { nassertv(_cull_handler != (CullHandler *)NULL); - r_traverse(root, _initial_state, 0); + r_traverse(root, _world_transform, _initial_state, 0); } //////////////////////////////////////////////////////////////////// @@ -92,7 +87,9 @@ traverse(PandaNode *root) { // Description: The recursive traversal implementation. //////////////////////////////////////////////////////////////////// void qpCullTraverser:: -r_traverse(PandaNode *node, const RenderState *state, int flags) { +r_traverse(PandaNode *node, const TransformState *transform, + const RenderState *state, int flags) { + CPT(TransformState) next_transform = transform->compose(node->get_transform()); CPT(RenderState) next_state = state->compose(node->get_state()); if (node->is_geom_node()) { @@ -104,7 +101,7 @@ r_traverse(PandaNode *node, const RenderState *state, int flags) { Geom *geom = geom_node->get_geom(i); CPT(RenderState) geom_state = next_state->compose(geom_node->get_geom_state(i)); - _cull_handler->record_geom(geom, geom_state); + _cull_handler->record_geom(geom, next_transform, geom_state); } } @@ -112,6 +109,6 @@ r_traverse(PandaNode *node, const RenderState *state, int flags) { PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { - r_traverse(cr.get_child(i), next_state, flags); + r_traverse(cr.get_child(i), next_transform, next_state, flags); } } diff --git a/panda/src/pgraph/qpcullTraverser.h b/panda/src/pgraph/qpcullTraverser.h index 42e2b30bbb..73c76b97f6 100644 --- a/panda/src/pgraph/qpcullTraverser.h +++ b/panda/src/pgraph/qpcullTraverser.h @@ -22,7 +22,7 @@ #include "pandabase.h" #include "renderState.h" -#include "transformAttrib.h" +#include "transformState.h" #include "pointerTo.h" class PandaNode; @@ -41,16 +41,17 @@ public: qpCullTraverser(); void set_initial_state(const RenderState *initial_state); - void set_world_transform(const TransformAttrib *world_transform); + void set_world_transform(const TransformState *world_transform); void set_cull_handler(CullHandler *cull_handler); void traverse(PandaNode *root); private: - void r_traverse(PandaNode *node, const RenderState *state, int flags); + void r_traverse(PandaNode *node, const TransformState *transform, + const RenderState *state, int flags); CPT(RenderState) _initial_state; - CPT(TransformAttrib) _world_transform; + CPT(TransformState) _world_transform; CullHandler *_cull_handler; }; diff --git a/panda/src/pgraph/qplensNode.I b/panda/src/pgraph/qplensNode.I new file mode 100644 index 0000000000..c1de65de68 --- /dev/null +++ b/panda/src/pgraph/qplensNode.I @@ -0,0 +1,89 @@ +// Filename: qplensNode.I +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpLensNode:: +qpLensNode(const string &name) : + PandaNode(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpLensNode:: +qpLensNode(const qpLensNode ©) : + PandaNode(copy), + _lens(copy._lens) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void qpLensNode:: +operator = (const qpLensNode ©) { + PandaNode::operator = (copy); + _lens = copy._lens; +} + +//////////////////////////////////////////////////////////////////// +// Function: copy_lens +// Access: Public +// Description: Sets up the qpLensNode using a copy of the +// indicated Lens. If the original Lens is +// changed or destroyed, this qpLensNode is not +// affected. +//////////////////////////////////////////////////////////////////// +INLINE void qpLensNode:: +copy_lens(const Lens &lens) { + _lens = lens.make_copy(); +} + +//////////////////////////////////////////////////////////////////// +// Function: set_lens +// Access: Public +// Description: Sets up the qpLensNode using this particular Lens +// pointer. If the lens is subsequently modified, the +// qpLensNode properties immediately reflect the change. +//////////////////////////////////////////////////////////////////// +INLINE void qpLensNode:: +set_lens(Lens *lens) { + _lens = lens; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_lens +// Access: Public +// Description: Returns a pointer to the particular Lens +// associated with this qpLensNode, or NULL if there is +// not yet a Lens associated. +//////////////////////////////////////////////////////////////////// +INLINE Lens *qpLensNode:: +get_lens() { + return _lens; +} diff --git a/panda/src/pgraph/qplensNode.cxx b/panda/src/pgraph/qplensNode.cxx new file mode 100644 index 0000000000..c2ec24a90b --- /dev/null +++ b/panda/src/pgraph/qplensNode.cxx @@ -0,0 +1,82 @@ +// Filename: qplensNode.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "qplensNode.h" +#include "geometricBoundingVolume.h" + +TypeHandle qpLensNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +PandaNode *qpLensNode:: +make_copy() const { + return new qpLensNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::is_in_view +// Access: Public +// Description: Returns true if the given point is within the bounds +// of the lens of the qpLensNode (i.e. if the camera can +// see the point). +//////////////////////////////////////////////////////////////////// +bool qpLensNode:: +is_in_view(const LPoint3f &pos) { + PT(BoundingVolume) bv = _lens->make_bounds(); + if (bv == NULL) { + return false; + } + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bv); + int ret = gbv->contains(pos); + return (ret != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpLensNode:: +output(ostream &out) const { + PandaNode::output(out); + if (_lens != (Lens *)NULL) { + out << " ("; + _lens->output(out); + out << ")"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpLensNode::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpLensNode:: +write(ostream &out, int indent_level) const { + PandaNode::write(out, indent_level); + if (_lens != (Lens *)NULL) { + _lens->write(out, indent_level + 2); + } +} + diff --git a/panda/src/pgraph/qplensNode.h b/panda/src/pgraph/qplensNode.h new file mode 100644 index 0000000000..6747473746 --- /dev/null +++ b/panda/src/pgraph/qplensNode.h @@ -0,0 +1,78 @@ +// Filename: qplensNode.h +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef QPLENSNODE_H +#define QPLENSNODE_H + +#include "pandabase.h" + +#include "pandaNode.h" +#include "lens.h" +#include "pointerTo.h" + +//////////////////////////////////////////////////////////////////// +// Class : qpLensNode +// Description : A node that contains a Lens. The most important +// example of this kind of node is a Camera, but other +// kinds of nodes also contain a lens (for instance, a +// Spotlight). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpLensNode : public PandaNode { +PUBLISHED: + INLINE qpLensNode(const string &name); + +public: + INLINE qpLensNode(const qpLensNode ©); + INLINE void operator = (const qpLensNode ©); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + + virtual PandaNode *make_copy() const; + +PUBLISHED: + INLINE void copy_lens(const Lens &lens); + INLINE void set_lens(Lens *lens); + INLINE Lens *get_lens(); + + bool is_in_view(const LPoint3f &pos); + +protected: + PT(Lens) _lens; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PandaNode::init_type(); + register_type(_type_handle, "qpLensNode", + PandaNode::get_class_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; +}; + +#include "qplensNode.I" + +#endif diff --git a/panda/src/pgraph/renderAttrib.cxx b/panda/src/pgraph/renderAttrib.cxx index 3993068044..1f635d355e 100644 --- a/panda/src/pgraph/renderAttrib.cxx +++ b/panda/src/pgraph/renderAttrib.cxx @@ -173,7 +173,8 @@ compare_to_impl(const RenderAttrib *other) const { // RenderAttrib (that is, a subsequent RenderAttrib // completely replaces the preceding one). On the other // hand, some kinds of RenderAttrib (for instance, -// TransformAttrib) might combine in meaningful ways. +// ColorTransformAttrib) might combine in meaningful +// ways. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) RenderAttrib:: compose_impl(const RenderAttrib *other) const { diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 91662a41ec..acc8cc68b6 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -72,9 +72,6 @@ RenderState:: if (_saved_entry != _states.end()) { _states.erase(_saved_entry); _saved_entry = _states.end(); - - cerr << "Removing " << (void *)this << ", " << _states.size() - << " remaining.\n"; } // Now make sure we clean up all other floating pointers to the @@ -359,8 +356,6 @@ invert_compose(const RenderState *other) const { // but we pretend that it is because it's only a cache which is // transparent to the rest of the interface. - cerr << "invert composing " << *this << " with " << *other << "\n"; - // We handle empty state (identity) as a trivial special case. if (is_empty()) { return other; @@ -384,7 +379,6 @@ invert_compose(const RenderState *other) const { ((Composition &)comp)._result = do_invert_compose(other); } // Here's the cache! - cerr << " returning cached result " << (void *)comp._result.p() << "\n"; return comp._result; } @@ -401,7 +395,6 @@ invert_compose(const RenderState *other) const { ((RenderState *)other)->_invert_composition_cache[this]._result = NULL; ((RenderState *)this)->_invert_composition_cache[other]._result = result; - cerr << " returning new result " << (void *)result.p() << "\n"; return result; } @@ -472,17 +465,19 @@ remove(TypeHandle type) const { //////////////////////////////////////////////////////////////////// void RenderState:: output(ostream &out) const { + out << get_type() << ":"; if (_attributes.empty()) { - out << "empty"; + out << "(empty)"; } else { Attributes::const_iterator ai = _attributes.begin(); - out << (*ai)._type; + out << "(" << (*ai)._type; ++ai; while (ai != _attributes.end()) { out << " " << (*ai)._type; ++ai; } + out << ")"; } } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 61b6634f50..61cee2fc6c 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -126,7 +126,8 @@ private: // should be). const RenderState *_self_compose; - // This is the actual set of data within the RenderState: a set of +private: + // This is the actual data within the RenderState: a set of // RenderAttribs. class Attribute { public: diff --git a/panda/src/pgraph/test_pgraph.cxx b/panda/src/pgraph/test_pgraph.cxx index 6b7d8ec3ef..b1d60316ae 100644 --- a/panda/src/pgraph/test_pgraph.cxx +++ b/panda/src/pgraph/test_pgraph.cxx @@ -17,9 +17,10 @@ //////////////////////////////////////////////////////////////////// #include "pandaNode.h" +#include "nodeChain.h" #include "textureAttrib.h" #include "colorAttrib.h" -#include "transformAttrib.h" +#include "transformState.h" #include "texture.h" #include "qpgeomNode.h" #include "geomTristrip.h" @@ -68,19 +69,25 @@ main(int argc, char *argv[]) { qpGeomNode *g2 = new qpGeomNode("g2"); b->add_child(g2); g2->add_geom(geom1, b->get_state()); - g2->set_attrib(TransformAttrib::make_mat(LMatrix4f::translate_mat(10, 0, 0))); + g2->set_transform(TransformState::make_mat(LMatrix4f::translate_mat(10, 0, 0))); cerr << "\n"; list_hierarchy(root, 0); - qpCullTraverser trav; - CullHandler cull_handler; - trav.set_cull_handler(&cull_handler); - cerr << "\n"; - trav.traverse(root); + NodeChain ch_g1(g1); + cerr << ch_g1 << "\n"; - cerr << "\n"; - trav.traverse(root); + NodeChain ch_g2(g2); + cerr << ch_g2 << "\n"; + + cerr << *ch_g1.get_rel_transform(ch_g2) << "\n"; + cerr << *ch_g2.get_rel_transform(ch_g1) << "\n"; + + cerr << *ch_g1.get_rel_state(ch_g2) << "\n"; + cerr << *ch_g2.get_rel_state(ch_g1) << "\n"; + + cerr << *ch_g2.get_rel_transform(ch_g2) << "\n"; + cerr << *ch_g2.get_rel_state(ch_g2) << "\n"; cerr << "\n"; return 0; diff --git a/panda/src/pgraph/transformAttrib.cxx b/panda/src/pgraph/transformAttrib.cxx deleted file mode 100644 index e15cc7b3a2..0000000000 --- a/panda/src/pgraph/transformAttrib.cxx +++ /dev/null @@ -1,308 +0,0 @@ -// Filename: transformAttrib.cxx -// Created by: drose (23Feb02) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://www.panda3d.org/license.txt . -// -// To contact the maintainers of this program write to -// panda3d@yahoogroups.com . -// -//////////////////////////////////////////////////////////////////// - -#include "transformAttrib.h" -#include "bamReader.h" -#include "bamWriter.h" -#include "datagram.h" -#include "datagramIterator.h" -#include "compose_matrix.h" - -TypeHandle TransformAttrib::_type_handle; - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::make_identity -// Access: Published, Static -// Description: Constructs an identity transform. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) TransformAttrib:: -make_identity() { - TransformAttrib *attrib = new TransformAttrib; - return return_new(attrib); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::make_components -// Access: Published, Static -// Description: Makes a new TransformAttrib with the specified -// components. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) TransformAttrib:: -make_components(const LVecBase3f &pos, const LVecBase3f &hpr, - const LVecBase3f &scale) { - // Make a special-case check for the identity transform. - if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) && - hpr == LVecBase3f(0.0f, 0.0f, 0.0f) && - scale == LVecBase3f(1.0f, 1.0f, 1.0f)) { - return make_identity(); - } - - TransformAttrib *attrib = new TransformAttrib; - attrib->_pos = pos; - attrib->_hpr = hpr; - attrib->_scale = scale; - attrib->_flags = F_components_given | F_components_known | F_has_components; - return return_new(attrib); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::make_components -// Access: Published, Static -// Description: Makes a new TransformAttrib with the specified -// transformation matrix. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) TransformAttrib:: -make_mat(const LMatrix4f &mat) { - // Make a special-case check for the identity matrix. - if (mat == LMatrix4f::ident_mat()) { - return make_identity(); - } - - TransformAttrib *attrib = new TransformAttrib; - attrib->_mat = mat; - attrib->_flags = F_mat_known; - return return_new(attrib); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::output -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -output(ostream &out) const { - out << get_type() << ":"; - if (is_identity()) { - out << "(identity)"; - - } else if (has_components()) { - out << "("; - if (get_pos() != LVecBase3f(0.0f, 0.0f, 0.0f)) { - out << "pos " << get_pos(); - } - if (get_hpr() != LVecBase3f(0.0f, 0.0f, 0.0f)) { - out << "hpr " << get_hpr(); - } - if (get_scale() != LVecBase3f(1.0f, 1.0f, 1.0f)) { - out << "scale " << get_scale(); - } - out << ")"; - - } else { - out << get_mat(); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::compare_to_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived TransformAttrib -// types to return a unique number indicating whether -// this TransformAttrib is equivalent to the other one. -// -// This should return 0 if the two TransformAttrib objects -// are equivalent, a number less than zero if this one -// should be sorted before the other one, and a number -// greater than zero otherwise. -// -// This will only be called with two TransformAttrib -// objects whose get_type() functions return the same. -//////////////////////////////////////////////////////////////////// -int TransformAttrib:: -compare_to_impl(const RenderAttrib *other) const { - const TransformAttrib *ta; - DCAST_INTO_R(ta, other, 0); - - if (is_identity() != ta->is_identity()) { - return is_identity() < ta->is_identity(); - } - if (is_identity()) { - // All identity transforms are equivalent to each other. - return 0; - } - - bool components_given = (_flags & F_components_given) != 0; - bool ta_components_given = (ta->_flags & F_components_given) != 0; - if (components_given != ta_components_given) { - return components_given < ta_components_given; - } - if (components_given) { - // If the transform was specified componentwise, compare them - // componentwise. - int c = _pos.compare_to(ta->_pos); - if (c != 0) { - return c; - } - c = _hpr.compare_to(ta->_hpr); - if (c != 0) { - return c; - } - c = _scale.compare_to(ta->_hpr); - return c; - - } - - // Otherwise, compare the matrices. - return get_mat().compare_to(ta->get_mat()); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::compose_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived RenderAttrib -// types to specify how two consecutive RenderAttrib -// objects of the same type interact. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) TransformAttrib:: -compose_impl(const RenderAttrib *other) const { - const TransformAttrib *ta; - DCAST_INTO_R(ta, other, other); - - // Identity times anything, duh. - if (is_identity()) { - return ta; - } else if (ta->is_identity()) { - return this; - } - - // Perhaps we should be smarter here if both transforms are - // componentwise, and multiply them componentwise. - LMatrix4f new_mat = get_mat() * ta->get_mat(); - return make_mat(new_mat); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::make_default_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived TransformAttrib -// types to specify what the default property for a -// TransformAttrib of this type should be. -// -// This should return a newly-allocated TransformAttrib of -// the same type that corresponds to whatever the -// standard default for this kind of TransformAttrib is. -//////////////////////////////////////////////////////////////////// -RenderAttrib *TransformAttrib:: -make_default_impl() const { - return new TransformAttrib; -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::calc_singular -// Access: Private -// Description: Determines whether the transform is singular (i.e. it -// scales to zero, and has no inverse). -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -calc_singular() { - bool singular = false; - - if (has_components()) { - // The matrix is singular if any component of its scale is 0. - singular = (_scale[0] == 0.0f || _scale[1] == 0.0f || _scale[2] == 0.0f); - } else { - // The matrix is singular if its determinant is zero. - const LMatrix4f &mat = get_mat(); - singular = (mat.get_upper_3().determinant() == 0.0f); - } - - if (singular) { - _flags |= F_is_singular; - } - _flags |= F_singular_known; -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::calc_components -// Access: Private -// Description: Derives the components from the matrix, if possible. -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -calc_components() { - bool possible = decompose_matrix(get_mat(), _scale, _hpr, _pos); - if (possible) { - // Some matrices can't be decomposed into scale, hpr, pos. - _flags |= F_has_components; - } - _flags |= F_components_known; -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::calc_mat -// Access: Private -// Description: Computes the matrix from the components. -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -calc_mat() { - compose_matrix(_mat, _scale, _hpr, _pos); - _flags |= F_mat_known; -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::register_with_read_factory -// Access: Public, Static -// Description: Tells the BamReader how to create objects of type -// TransformAttrib. -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -register_with_read_factory() { - BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::write_datagram -// Access: Public, Virtual -// Description: Writes the contents of this object to the datagram -// for shipping out to a Bam file. -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -write_datagram(BamWriter *manager, Datagram &dg) { - RenderAttrib::write_datagram(manager, dg); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::make_from_bam -// Access: Protected, Static -// Description: This function is called by the BamReader's factory -// when a new object of type TransformAttrib is encountered -// in the Bam file. It should create the TransformAttrib -// and extract its information from the file. -//////////////////////////////////////////////////////////////////// -TypedWritable *TransformAttrib:: -make_from_bam(const FactoryParams ¶ms) { - TransformAttrib *attrib = new TransformAttrib; - DatagramIterator scan; - BamReader *manager; - - parse_params(params, scan, manager); - attrib->fillin(scan, manager); - - return new_from_bam(attrib, manager); -} - -//////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::fillin -// Access: Protected -// Description: This internal function is called by make_from_bam to -// read in all of the relevant data from the BamFile for -// the new TransformAttrib. -//////////////////////////////////////////////////////////////////// -void TransformAttrib:: -fillin(DatagramIterator &scan, BamReader *manager) { - RenderAttrib::fillin(scan, manager); -} diff --git a/panda/src/pgraph/transformAttrib.h b/panda/src/pgraph/transformAttrib.h deleted file mode 100644 index e3a4fb95ef..0000000000 --- a/panda/src/pgraph/transformAttrib.h +++ /dev/null @@ -1,117 +0,0 @@ -// Filename: transformAttrib.h -// Created by: drose (23Feb02) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://www.panda3d.org/license.txt . -// -// To contact the maintainers of this program write to -// panda3d@yahoogroups.com . -// -//////////////////////////////////////////////////////////////////// - -#ifndef TRANSFORMATTRIB_H -#define TRANSFORMATTRIB_H - -#include "pandabase.h" - -#include "renderAttrib.h" -#include "luse.h" - -//////////////////////////////////////////////////////////////////// -// Class : TransformAttrib -// Description : Indicates a coordinate-system transform on vertices. -// TransformAttribs are the primary means for storing -// transformations on the scene graph. -// -// Transforms may be specified in one of two ways: -// componentwise, with a pos-hpr-scale, or with an -// arbitrary transform matrix. If you specify a -// transform componentwise, it will remember its -// original components. -//////////////////////////////////////////////////////////////////// -class EXPCL_PANDA TransformAttrib : public RenderAttrib { -private: - INLINE TransformAttrib(); - -PUBLISHED: - static CPT(RenderAttrib) make_identity(); - static CPT(RenderAttrib) make_components(const LVecBase3f &pos, - const LVecBase3f &hpr, - const LVecBase3f &scale); - static CPT(RenderAttrib) make_mat(const LMatrix4f &mat); - - INLINE bool is_identity() const; - INLINE bool is_singular() const; - INLINE bool has_components() const; - INLINE const LVecBase3f &get_pos() const; - INLINE const LVecBase3f &get_hpr() const; - INLINE const LVecBase3f &get_scale() const; - INLINE const LMatrix4f &get_mat() const; - -public: - virtual void output(ostream &out) const; - -protected: - virtual int compare_to_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; - virtual RenderAttrib *make_default_impl() const; - -private: - INLINE void check_singular() const; - INLINE void check_components() const; - INLINE void check_mat() const; - void calc_singular(); - void calc_components(); - void calc_mat(); - - enum Flags { - F_is_identity = 0x0001, - F_is_singular = 0x0002, - F_singular_known = 0x0004, - F_components_given = 0x0008, - F_components_known = 0x0010, - F_has_components = 0x0020, - F_mat_known = 0x0040, - }; - LVecBase3f _pos, _hpr, _scale; - LMatrix4f _mat; - - short _flags; - -public: - static void register_with_read_factory(); - virtual void write_datagram(BamWriter *manager, Datagram &dg); - -protected: - static TypedWritable *make_from_bam(const FactoryParams ¶ms); - void fillin(DatagramIterator &scan, BamReader *manager); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - RenderAttrib::init_type(); - register_type(_type_handle, "TransformAttrib", - RenderAttrib::get_class_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; -}; - -#include "transformAttrib.I" - -#endif - diff --git a/panda/src/pgraph/transformAttrib.I b/panda/src/pgraph/transformState.I similarity index 60% rename from panda/src/pgraph/transformAttrib.I rename to panda/src/pgraph/transformState.I index fd1581cfc3..a6f4dd51e9 100644 --- a/panda/src/pgraph/transformAttrib.I +++ b/panda/src/pgraph/transformState.I @@ -1,5 +1,5 @@ -// Filename: transformAttrib.I -// Created by: drose (23Feb02) +// Filename: transformState.I +// Created by: drose (25Feb02) // //////////////////////////////////////////////////////////////////// // @@ -18,42 +18,95 @@ //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::Constructor -// Access: Private -// Description: Use TransformAttrib::make() to construct a new -// TransformAttrib object. +// Function: TransformState::make_pos +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. //////////////////////////////////////////////////////////////////// -INLINE TransformAttrib:: -TransformAttrib() { - _flags = F_is_identity | F_singular_known; +INLINE CPT(TransformState) TransformState:: +make_pos(const LVecBase3f &pos) { + return make_pos_hpr_scale(pos, + LVecBase3f(0.0f, 0.0f, 0.0f), + LVecBase3f(1.0f, 1.0f, 1.0f)); } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::is_identity +// Function: TransformState::make_hpr +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) TransformState:: +make_hpr(const LVecBase3f &hpr) { + return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f), + hpr, + LVecBase3f(1.0f, 1.0f, 1.0f)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_pos_hpr +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) TransformState:: +make_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr) { + return make_pos_hpr_scale(pos, hpr, + LVecBase3f(1.0, 1.0f, 1.0f)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_scale +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) TransformState:: +make_scale(float scale) { + return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f), + LVecBase3f(0.0f, 0.0f, 0.0f), + LVecBase3f(scale, scale, scale)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_scale +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) TransformState:: +make_scale(const LVecBase3f &scale) { + return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f), + LVecBase3f(0.0f, 0.0f, 0.0f), + scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::is_identity // Access: Published // Description: Returns true if the transform represents the identity // matrix, false otherwise. //////////////////////////////////////////////////////////////////// -INLINE bool TransformAttrib:: +INLINE bool TransformState:: is_identity() const { return ((_flags & F_is_identity) != 0); } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::is_singular +// Function: TransformState::is_singular // Access: Published // Description: Returns true if the transform represents a singular // transform (that is, it has a zero scale, and it // cannot be inverted), or false otherwise. //////////////////////////////////////////////////////////////////// -INLINE bool TransformAttrib:: +INLINE bool TransformState:: is_singular() const { check_singular(); return ((_flags & F_is_singular) != 0); } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::has_components +// Function: TransformState::has_components // Access: Published // Description: Returns true if the transform can be described by // separate pos, hpr, and scale components. Most @@ -69,20 +122,20 @@ is_singular() const { // If this returns true, you may safely call get_pos(), // etc., to retrieve the components. //////////////////////////////////////////////////////////////////// -INLINE bool TransformAttrib:: +INLINE bool TransformState:: has_components() const { check_components(); return ((_flags & F_has_components) != 0); } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::get_pos +// Function: TransformState::get_pos // Access: Published // Description: Returns the pos component of the transform. It is an // error to call this if has_components() returned // false. //////////////////////////////////////////////////////////////////// -INLINE const LVecBase3f &TransformAttrib:: +INLINE const LVecBase3f &TransformState:: get_pos() const { check_components(); nassertr(has_components(), _pos); @@ -90,13 +143,13 @@ get_pos() const { } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::get_hpr +// Function: TransformState::get_hpr // Access: Published // Description: Returns the hpr component of the transform. It is an // error to call this if has_components() returned // false. //////////////////////////////////////////////////////////////////// -INLINE const LVecBase3f &TransformAttrib:: +INLINE const LVecBase3f &TransformState:: get_hpr() const { check_components(); nassertr(has_components(), _hpr); @@ -104,13 +157,13 @@ get_hpr() const { } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::get_scale +// Function: TransformState::get_scale // Access: Published // Description: Returns the scale component of the transform. It is an // error to call this if has_components() returned // false. //////////////////////////////////////////////////////////////////// -INLINE const LVecBase3f &TransformAttrib:: +INLINE const LVecBase3f &TransformState:: get_scale() const { check_components(); nassertr(has_components(), _scale); @@ -118,55 +171,55 @@ get_scale() const { } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::get_mat +// Function: TransformState::get_mat // Access: Published // Description: Returns the matrix that describes the transform. //////////////////////////////////////////////////////////////////// -INLINE const LMatrix4f &TransformAttrib:: +INLINE const LMatrix4f &TransformState:: get_mat() const { check_mat(); return _mat; } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::check_singular +// Function: TransformState::check_singular // Access: Private // Description: Ensures that we know whether the matrix is singular. //////////////////////////////////////////////////////////////////// -INLINE void TransformAttrib:: +INLINE void TransformState:: check_singular() const { // This pretends to be a const function, even though it's not, // because it only updates a transparent cache value. if ((_flags & F_singular_known) == 0) { - ((TransformAttrib *)this)->calc_singular(); + ((TransformState *)this)->calc_singular(); } } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::check_components +// Function: TransformState::check_components // Access: Private // Description: Ensures that we know the components of the transform // (or that we know they cannot be derived). //////////////////////////////////////////////////////////////////// -INLINE void TransformAttrib:: +INLINE void TransformState:: check_components() const { // This pretends to be a const function, even though it's not, // because it only updates a transparent cache value. if ((_flags & F_components_known) == 0) { - ((TransformAttrib *)this)->calc_components(); + ((TransformState *)this)->calc_components(); } } //////////////////////////////////////////////////////////////////// -// Function: TransformAttrib::check_mat +// Function: TransformState::check_mat // Access: Private // Description: Ensures that we know the overall matrix. //////////////////////////////////////////////////////////////////// -INLINE void TransformAttrib:: +INLINE void TransformState:: check_mat() const { // This pretends to be a const function, even though it's not, // because it only updates a transparent cache value. if ((_flags & F_mat_known) == 0) { - ((TransformAttrib *)this)->calc_mat(); + ((TransformState *)this)->calc_mat(); } } diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx new file mode 100644 index 0000000000..899460380e --- /dev/null +++ b/panda/src/pgraph/transformState.cxx @@ -0,0 +1,650 @@ +// Filename: transformState.cxx +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "transformState.h" +#include "compose_matrix.h" +#include "bamReader.h" +#include "bamWriter.h" +#include "datagramIterator.h" +#include "indent.h" +#include "compareTo.h" + +TransformState::States TransformState::_states; +CPT(TransformState) TransformState::_identity_state; +TypeHandle TransformState::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::Constructor +// Access: Protected +// Description: Actually, this could be a private constructor, since +// no one inherits from TransformState, but gcc gives us a +// spurious warning if all constructors are private. +//////////////////////////////////////////////////////////////////// +TransformState:: +TransformState() { + _saved_entry = _states.end(); + _self_compose = (TransformState *)NULL; + _flags = F_is_identity | F_singular_known; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::Copy Constructor +// Access: Private +// Description: TransformStates are not meant to be copied. +//////////////////////////////////////////////////////////////////// +TransformState:: +TransformState(const TransformState &) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::Copy Assignment Operator +// Access: Private +// Description: TransformStates are not meant to be copied. +//////////////////////////////////////////////////////////////////// +void TransformState:: +operator = (const TransformState &) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::Destructor +// Access: Public, Virtual +// Description: The destructor is responsible for removing the +// TransformState from the global set if it is there. +//////////////////////////////////////////////////////////////////// +TransformState:: +~TransformState() { + // Remove the deleted TransformState object from the global pool. + if (_saved_entry != _states.end()) { + _states.erase(_saved_entry); + _saved_entry = _states.end(); + } + + // Now make sure we clean up all other floating pointers to the + // TransformState. These may be scattered around in the various + // CompositionCaches from other TransformState objects. + + // Fortunately, since we added CompositionCache records in pairs, we + // know exactly the set of TransformState objects that have us in their + // cache: it's the same set of TransformState objects that we have in + // our own cache. + + // We do need to put some thought into this loop, because as we + // clear out cache entries we'll cause other TransformState objects to + // destruct, which could cause things to get pulled out of our own + // _composition_cache map. We don't want to get bitten by this + // cascading effect. + CompositionCache::iterator ci; + ci = _composition_cache.begin(); + while (ci != _composition_cache.end()) { + { + PT(TransformState) other = (TransformState *)(*ci).first; + Composition comp = (*ci).second; + + // We should never have a reflexive entry in this map. If we + // do, something got screwed up elsewhere. + nassertv(other != (const TransformState *)this); + + // Now we're holding a reference count to the other state, as well + // as to the computed result (if any), so neither object will be + // tempted to destruct. Go ahead and remove ourselves from the + // other cache. + other->_composition_cache.erase(this); + + // It's all right if the other state destructs now, since it + // won't try to remove itself from our own composition cache any + // more. Someone might conceivably delete the *next* entry, + // though, so we should be sure to let all that deleting finish + // up before we attempt to increment ci, by closing the scope + // here. + } + // Now it's safe to increment ci, because the current cache entry + // has not gone away, and if the next one has, by now it's safely + // gone. + ++ci; + } + + // A similar bit of code for the invert cache. + ci = _invert_composition_cache.begin(); + while (ci != _invert_composition_cache.end()) { + { + PT(TransformState) other = (TransformState *)(*ci).first; + Composition comp = (*ci).second; + nassertv(other != (const TransformState *)this); + other->_invert_composition_cache.erase(this); + } + ++ci; + } + + // Also, if we called compose(this) at some point and the return + // value was something other than this, we need to decrement the + // associated reference count. + if (_self_compose != (TransformState *)NULL && _self_compose != this) { + unref_delete((TransformState *)_self_compose); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::operator < +// Access: Public +// Description: Provides an arbitrary ordering among all unique +// TransformStates, so we can store the essentially +// different ones in a big set and throw away the rest. +// +// This method is not needed outside of the TransformState +// class because all equivalent TransformState objects are +// guaranteed to share the same pointer; thus, a pointer +// comparison is always sufficient. +//////////////////////////////////////////////////////////////////// +bool TransformState:: +operator < (const TransformState &other) const { + bool components_given = (_flags & F_components_given) != 0; + bool other_components_given = (other._flags & F_components_given) != 0; + if (components_given != other_components_given) { + return components_given < other_components_given; + } + if (components_given) { + // If the transform was specified componentwise, compare them + // componentwise. + int c = _pos.compare_to(other._pos); + if (c != 0) { + return c < 0; + } + c = _hpr.compare_to(other._hpr); + if (c != 0) { + return c < 0; + } + c = _scale.compare_to(other._hpr); + return c < 0; + } + + // Otherwise, compare the matrices. + return get_mat() < other.get_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_identity +// Access: Published, Static +// Description: Constructs an identity transform. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +make_identity() { + // The identity state is asked for so often, we make it a special case + // and store a pointer forever once we find it the first time. + if (_identity_state == (TransformState *)NULL) { + TransformState *state = new TransformState; + _identity_state = return_new(state); + } + + return _identity_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_pos_hpr_scale +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, + const LVecBase3f &scale) { + // Make a special-case check for the identity transform. + if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) && + hpr == LVecBase3f(0.0f, 0.0f, 0.0f) && + scale == LVecBase3f(1.0f, 1.0f, 1.0f)) { + return make_identity(); + } + + TransformState *attrib = new TransformState; + attrib->_pos = pos; + attrib->_hpr = hpr; + attrib->_scale = scale; + attrib->_flags = F_components_given | F_components_known | F_has_components; + return return_new(attrib); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_mat +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// transformation matrix. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +make_mat(const LMatrix4f &mat) { + // Make a special-case check for the identity matrix. + if (mat == LMatrix4f::ident_mat()) { + return make_identity(); + } + + TransformState *attrib = new TransformState; + attrib->_mat = mat; + attrib->_flags = F_mat_known; + return return_new(attrib); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::compose +// Access: Published +// Description: Returns a new TransformState object that represents the +// composition of this state with the other state. +// +// The result of this operation is cached, and will be +// retained as long as both this TransformState object and +// the other TransformState object continue to exist. +// Should one of them destruct, the cached entry will be +// removed, and its pointer will be allowed to destruct +// as well. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +compose(const TransformState *other) const { + // This method isn't strictly const, because it updates the cache, + // but we pretend that it is because it's only a cache which is + // transparent to the rest of the interface. + + // We handle identity as a trivial special case. + if (is_identity()) { + return other; + } + if (other->is_identity()) { + return this; + } + + if (other == this) { + // compose(this) has to be handled as a special case, because the + // caching problem is so different. + if (_self_compose != (TransformState *)NULL) { + return _self_compose; + } + CPT(TransformState) result = do_compose(this); + ((TransformState *)this)->_self_compose = result; + + if (result != (const TransformState *)this) { + // If the result of compose(this) is something other than this, + // explicitly increment the reference count. We have to be sure + // to decrement it again later, in our destructor. + _self_compose->ref(); + + // (If the result was just this again, we still store the + // result, but we don't increment the reference count, since + // that would be a self-referential leak. What a mess this is.) + } + return _self_compose; + } + + // Is this composition already cached? + CompositionCache::const_iterator ci = _composition_cache.find(other); + if (ci != _composition_cache.end()) { + const Composition &comp = (*ci).second; + if (comp._result == (const TransformState *)NULL) { + // Well, it wasn't cached already, but we already had an entry + // (probably created for the reverse direction), so use the same + // entry to store the new result. + ((Composition &)comp)._result = do_compose(other); + } + // Here's the cache! + return comp._result; + } + + // We need to make a new cache entry, both in this object and in the + // other object. We make both records so the other TransformState + // object will know to delete the entry from this object when it + // destructs, and vice-versa. + + // The cache entry in this object is the only one that indicates the + // result; the other will be NULL for now. + CPT(TransformState) result = do_compose(other); + // We store them in this order, on the off-chance that other is the + // same as this, a degenerate case which is still worth supporting. + ((TransformState *)other)->_composition_cache[this]._result = NULL; + ((TransformState *)this)->_composition_cache[other]._result = result; + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::invert_compose +// Access: Published +// Description: Returns a new TransformState object that represents the +// composition of this state's inverse with the other +// state. +// +// This is similar to compose(), but is particularly +// useful for computing the relative state of a node as +// viewed from some other node. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +invert_compose(const TransformState *other) const { + // This method isn't strictly const, because it updates the cache, + // but we pretend that it is because it's only a cache which is + // transparent to the rest of the interface. + + // We handle identity as a trivial special case. + if (is_identity()) { + return other; + } + // Unlike compose(), the case of other->is_identity() is not quite as + // trivial for invert_compose(). + + if (other == this) { + // a->invert_compose(a) always produces identity. + return make_identity(); + } + + // Is this composition already cached? + CompositionCache::const_iterator ci = _invert_composition_cache.find(other); + if (ci != _invert_composition_cache.end()) { + const Composition &comp = (*ci).second; + if (comp._result == (const TransformState *)NULL) { + // Well, it wasn't cached already, but we already had an entry + // (probably created for the reverse direction), so use the same + // entry to store the new result. + ((Composition &)comp)._result = do_invert_compose(other); + } + // Here's the cache! + return comp._result; + } + + // We need to make a new cache entry, both in this object and in the + // other object. We make both records so the other TransformState + // object will know to delete the entry from this object when it + // destructs, and vice-versa. + + // The cache entry in this object is the only one that indicates the + // result; the other will be NULL for now. + CPT(TransformState) result = do_invert_compose(other); + // We store them in this order, on the off-chance that other is the + // same as this, a degenerate case which is still worth supporting. + ((TransformState *)other)->_invert_composition_cache[this]._result = NULL; + ((TransformState *)this)->_invert_composition_cache[other]._result = result; + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::output +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void TransformState:: +output(ostream &out) const { + out << get_type() << ":"; + if (is_identity()) { + out << "(identity)"; + + } else if (has_components()) { + out << "("; + if (get_pos() != LVecBase3f(0.0f, 0.0f, 0.0f)) { + out << "pos " << get_pos(); + } + if (get_hpr() != LVecBase3f(0.0f, 0.0f, 0.0f)) { + out << "hpr " << get_hpr(); + } + if (get_scale() != LVecBase3f(1.0f, 1.0f, 1.0f)) { + out << "scale " << get_scale(); + } + out << ")"; + + } else { + out << get_mat(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::write +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void TransformState:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::return_new +// Access: Private, Static +// Description: This function is used to share a common TransformState +// pointer for all equivalent TransformState objects. +// +// See the similar logic in RenderAttrib. The idea is +// to create a new TransformState object and pass it +// through this function, which will share the pointer +// with a previously-created TransformState object if it is +// equivalent. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +return_new(TransformState *state) { + nassertr(state != (TransformState *)NULL, state); + + // This should be a newly allocated pointer, not one that was used + // for anything else. + nassertr(state->_saved_entry == _states.end(), state); + + // Save the state in a local PointerTo so that it will be freed at + // the end of this function if no one else uses it. + CPT(TransformState) pt_state = state; + + pair result = _states.insert(state); + if (result.second) { + // The state was inserted; save the iterator and return the + // input state. + state->_saved_entry = result.first; + return pt_state; + } + + // The state was not inserted; there must be an equivalent one + // already in the set. Return that one. + return *(result.first); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::do_compose +// Access: Private +// Description: The private implemention of compose(); this actually +// composes two TransformStates, without bothering with the +// cache. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +do_compose(const TransformState *other) const { + LMatrix4f new_mat = get_mat() * other->get_mat(); + return make_mat(new_mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::do_invert_compose +// Access: Private +// Description: The private implemention of invert_compose(). +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +do_invert_compose(const TransformState *other) const { + LMatrix4f new_mat; + new_mat.invert_from(get_mat()); + new_mat = new_mat * other->get_mat(); + return make_mat(new_mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::calc_singular +// Access: Private +// Description: Determines whether the transform is singular (i.e. it +// scales to zero, and has no inverse). +//////////////////////////////////////////////////////////////////// +void TransformState:: +calc_singular() { + bool singular = false; + + if (has_components()) { + // The matrix is singular if any component of its scale is 0. + singular = (_scale[0] == 0.0f || _scale[1] == 0.0f || _scale[2] == 0.0f); + } else { + // The matrix is singular if its determinant is zero. + const LMatrix4f &mat = get_mat(); + singular = (mat.get_upper_3().determinant() == 0.0f); + } + + if (singular) { + _flags |= F_is_singular; + } + _flags |= F_singular_known; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::calc_components +// Access: Private +// Description: Derives the components from the matrix, if possible. +//////////////////////////////////////////////////////////////////// +void TransformState:: +calc_components() { + if ((_flags & F_is_identity) != 0) { + _scale.set(1.0f, 1.0f, 1.0f); + _hpr.set(0.0f, 0.0f, 0.0f); + _pos.set(0.0f, 0.0f, 0.0f); + _flags |= F_has_components; + + } else { + // If we don't have components and we're not identity, the only + // other explanation is that we were constructed via a matrix. + nassertv((_flags & F_mat_known) != 0); + + bool possible = decompose_matrix(get_mat(), _scale, _hpr, _pos); + if (possible) { + // Some matrices can't be decomposed into scale, hpr, pos. + _flags |= F_has_components; + } + } + + _flags |= F_components_known; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::calc_mat +// Access: Private +// Description: Computes the matrix from the components. +//////////////////////////////////////////////////////////////////// +void TransformState:: +calc_mat() { + if ((_flags & F_is_identity) != 0) { + _mat = LMatrix4f::ident_mat(); + + } else { + // If we don't have a matrix and we're not identity, the only + // other explanation is that we were constructed via components. + nassertv((_flags & F_components_known) != 0); + compose_matrix(_mat, _scale, _hpr, _pos); + } + _flags |= F_mat_known; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// TransformState. +//////////////////////////////////////////////////////////////////// +void TransformState:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void TransformState:: +write_datagram(BamWriter *manager, Datagram &dg) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::finalize +// Access: Public, Virtual +// Description: Method to ensure that any necessary clean up tasks +// that have to be performed by this object are performed +//////////////////////////////////////////////////////////////////// +void TransformState:: +finalize() { + // Unref the pointer that we explicitly reffed in make_from_bam(). + unref(); + + // We should never get back to zero after unreffing our own count, + // because we expect to have been stored in a pointer somewhere. If + // we do get to zero, it's a memory leak; the way to avoid this is + // to call unref_delete() above instead of unref(), but this is + // dangerous to do from within a virtual function. + nassertv(get_ref_count() != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_from_bam +// Access: Protected, Static +// Description: This function is called by the BamReader's factory +// when a new object of type TransformState is encountered +// in the Bam file. It should create the TransformState +// and extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWritable *TransformState:: +make_from_bam(const FactoryParams ¶ms) { + TransformState *state = new TransformState; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + state->fillin(scan, manager); + + return new_from_bam(state, manager); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::new_from_bam +// Access: Protected, Static +// Description: Uniquifies the pointer for a TransformState object just +// created from a bam file, and preserves its reference +// count correctly. +//////////////////////////////////////////////////////////////////// +TypedWritable *TransformState:: +new_from_bam(TransformState *state, BamReader *manager) { + // First, uniquify the pointer. + CPT(TransformState) pointer = return_new(state); + + // But now we have a problem, since we have to hold the reference + // count and there's no way to return a TypedWritable while still + // holding the reference count! We work around this by explicitly + // upping the count, and also setting a finalize() callback to down + // it later. + if (pointer == state) { + pointer->ref(); + manager->register_finalize(state); + } + + // We have to cast the pointer back to non-const, because the bam + // reader expects that. + return (TransformState *)pointer.p(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new TransformState. +//////////////////////////////////////////////////////////////////// +void TransformState:: +fillin(DatagramIterator &scan, BamReader *manager) { +} diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h new file mode 100644 index 0000000000..6b7c1a1a79 --- /dev/null +++ b/panda/src/pgraph/transformState.h @@ -0,0 +1,187 @@ +// Filename: transformState.h +// Created by: drose (25Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSFORMSTATE_H +#define TRANSFORMSTATE_H + +#include "pandabase.h" + +#include "typedWritableReferenceCount.h" +#include "pointerTo.h" +#include "indirectLess.h" +#include "luse.h" +#include "pset.h" + +class GraphicsStateGuardianBase; + +//////////////////////////////////////////////////////////////////// +// Class : TransformState +// Description : Indicates a coordinate-system transform on vertices. +// TransformStates are the primary means for storing +// transformations on the scene graph. +// +// Transforms may be specified in one of two ways: +// componentwise, with a pos-hpr-scale, or with an +// arbitrary transform matrix. If you specify a +// transform componentwise, it will remember its +// original components. +// +// TransformState objects are managed very much like +// RenderState objects. They are immutable and +// reference-counted automatically. +// +// You should not attempt to create or modify a +// TransformState object directly. Instead, call one of +// the make() functions to create one for you. And +// instead of modifying a TransformState object, create a +// new one. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransformState : public TypedWritableReferenceCount { +protected: + TransformState(); + +private: + TransformState(const TransformState ©); + void operator = (const TransformState ©); + +public: + virtual ~TransformState(); + + bool operator < (const TransformState &other) const; + +PUBLISHED: + static CPT(TransformState) make_identity(); + INLINE static CPT(TransformState) make_pos(const LVecBase3f &pos); + INLINE static CPT(TransformState) make_hpr(const LVecBase3f &hpr); + INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3f &pos, + const LVecBase3f &hpr); + INLINE static CPT(TransformState) make_scale(float scale); + INLINE static CPT(TransformState) make_scale(const LVecBase3f &scale); + static CPT(TransformState) make_pos_hpr_scale(const LVecBase3f &pos, + const LVecBase3f &hpr, + const LVecBase3f &scale); + static CPT(TransformState) make_mat(const LMatrix4f &mat); + + INLINE bool is_identity() const; + INLINE bool is_singular() const; + INLINE bool has_components() const; + INLINE const LVecBase3f &get_pos() const; + INLINE const LVecBase3f &get_hpr() const; + INLINE const LVecBase3f &get_scale() const; + INLINE const LMatrix4f &get_mat() const; + + CPT(TransformState) compose(const TransformState *other) const; + CPT(TransformState) invert_compose(const TransformState *other) const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level) const; + +private: + static CPT(TransformState) return_new(TransformState *state); + CPT(TransformState) do_compose(const TransformState *other) const; + CPT(TransformState) do_invert_compose(const TransformState *other) const; + +private: + typedef pset > States; + static States _states; + static CPT(TransformState) _identity_state; + + // This iterator records the entry corresponding to this TransformState + // object in the above global set. We keep the iterator around so + // we can remove it when the TransformState destructs. + States::iterator _saved_entry; + + // This data structure manages the job of caching the composition of + // two TransformStates. It's complicated because we have to be sure to + // remove the entry if *either* of the input TransformStates destructs. + // To implement this, we always record Composition entries in pairs, + // one in each of the two involved TransformState objects. + class Composition { + public: + CPT(TransformState) _result; + }; + + typedef pmap CompositionCache; + CompositionCache _composition_cache; + CompositionCache _invert_composition_cache; + + // Thise pointer is used to cache the result of compose(this). This + // has to be a special case, because we have to handle the reference + // counts carefully so that we don't leak. + const TransformState *_self_compose; + +private: + // This is the actual data within the TransformState. + INLINE void check_singular() const; + INLINE void check_components() const; + INLINE void check_mat() const; + void calc_singular(); + void calc_components(); + void calc_mat(); + + enum Flags { + F_is_identity = 0x0001, + F_is_singular = 0x0002, + F_singular_known = 0x0004, + F_components_given = 0x0008, + F_components_known = 0x0010, + F_has_components = 0x0020, + F_mat_known = 0x0040, + }; + LVecBase3f _pos, _hpr, _scale; + LMatrix4f _mat; + + short _flags; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + virtual void finalize(); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + static TypedWritable *new_from_bam(TransformState *state, BamReader *manager); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWritableReferenceCount::init_type(); + register_type(_type_handle, "TransformState", + TypedWritableReferenceCount::get_class_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; +}; + +INLINE ostream &operator << (ostream &out, const TransformState &state) { + state.output(out); + return out; +} + +#include "transformState.I" + +#endif + diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index 963460edc4..f0accd5c59 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -23,7 +23,7 @@ #include "graphicsPipe.h" #include "interactiveGraphicsPipe.h" #include "displayRegion.h" -#include "camera.h" +#include "qpcamera.h" #include "perspectiveLens.h" #include "pandaNode.h" #include "textureAttrib.h" @@ -33,6 +33,32 @@ #include "texture.h" #include "texturePool.h" +// These are in support of legacy data graph operations. +#include "namedNode.h" +#include "mouse.h" +#include "mouseWatcher.h" +#include "trackball.h" +#include "transform2sg.h" +#include "dataRelation.h" +#include "dataGraphTraversal.h" +#include "buttonThrower.h" +#include "modifierButtons.h" +#include "keyboardButton.h" +#include "event.h" +#include "eventQueue.h" +#include "eventHandler.h" + +// Use dconfig to read a few Configrc variables. +Configure(config_pview); +ConfigureFn(config_pview) { +} + +static const int win_width = config_pview.GetInt("win-width", 640); +static const int win_height = config_pview.GetInt("win-height", 480); + +// As long as this is true, the main loop will continue running. +bool run_flag = true; + PT(GraphicsPipe) make_pipe() { // We use the GraphicsPipe factory to make us a renderable pipe @@ -59,20 +85,25 @@ make_pipe() { return pipe; } -PT(Camera) -setup_window(GraphicsPipe *pipe, GraphicsEngine *engine) { +PT(GraphicsWindow) +make_window(GraphicsPipe *pipe, GraphicsEngine *engine) { // Now create a window on that pipe. GraphicsWindow::Properties window_prop; window_prop._xorg = 0; window_prop._yorg = 0; - window_prop._xsize = 640; - window_prop._ysize = 480; + window_prop._xsize = win_width; + window_prop._ysize = win_height; window_prop._title = "Panda Viewer"; // window_prop._fullscreen = true; PT(GraphicsWindow) window = pipe->make_window(window_prop); engine->add_window(window); + + return window; +} +PT(qpCamera) +make_camera(GraphicsWindow *window) { // Get the first channel on the window. This will be the only // channel on non-SGI hardware. PT(GraphicsChannel) channel = window->get_channel(0); @@ -84,11 +115,11 @@ setup_window(GraphicsPipe *pipe, GraphicsEngine *engine) { PT(DisplayRegion) dr = layer->make_display_region(); // Finally, we need a camera to associate with the display region. - PT(Camera) camera = new Camera; + PT(qpCamera) camera = new qpCamera("camera"); PT(Lens) lens = new PerspectiveLens; - lens->set_film_size(640, 480); + lens->set_film_size(win_width, win_height); camera->set_lens(lens); - dr->set_camera(camera); + dr->set_qpcamera(NodeChain(camera)); return camera; } @@ -102,9 +133,9 @@ make_default_geometry(PandaNode *parent) { PTA_Colorf colors; PTA_ushort cindex; - coords.push_back(Vertexf::rfu(0.0, 20.0, 0.0)); - coords.push_back(Vertexf::rfu(1.0, 20.0, 0.0)); - coords.push_back(Vertexf::rfu(0.0, 20.0, 1.0)); + coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0)); uvs.push_back(TexCoordf(0.0, 0.0)); uvs.push_back(TexCoordf(1.0, 0.0)); uvs.push_back(TexCoordf(0.0, 1.0)); @@ -153,6 +184,39 @@ get_models(PandaNode *parent, int argc, char *argv[]) { */ } +NamedNode * +setup_mouse(NamedNode *data_root, GraphicsWindow *window) { + MouseAndKeyboard *mouse = new MouseAndKeyboard(window, 0); + new DataRelation(data_root, mouse); + + // Create a ButtonThrower to throw events from the keyboard. + PT(ButtonThrower) bt = new ButtonThrower("kb-events"); + ModifierButtons mods; + mods.add_button(KeyboardButton::shift()); + mods.add_button(KeyboardButton::control()); + mods.add_button(KeyboardButton::alt()); + bt->set_modifier_buttons(mods); + new DataRelation(mouse, bt); + + return mouse; +} + +void +setup_trackball(NamedNode *mouse, qpCamera *camera) { + PT(Trackball) trackball = new Trackball("trackball"); + trackball->set_pos(LVector3f::forward() * 50.0); + new DataRelation(mouse, trackball); + + PT(Transform2SG) tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_node(camera); + new DataRelation(trackball, tball2cam); +} + +void +event_esc(CPT_Event) { + // The Escape or q key was pressed. Exit the application. + run_flag = false; +} int main(int argc, char *argv[]) { @@ -163,33 +227,40 @@ main(int argc, char *argv[]) { GraphicsEngine *engine = new GraphicsEngine; // Now open a window and get a camera. - PT(Camera) camera = setup_window(pipe, engine); + PT(GraphicsWindow) window = make_window(pipe, engine); + PT(qpCamera) camera = make_camera(window); // Now we just need to make a scene graph for the camera to render. PT(PandaNode) render = new PandaNode("render"); - camera->set_qpscene(render); + render->add_child(camera); + camera->set_scene(render); + + // Set up a data graph for tracking user input. For now, this uses + // the old-style graph interface. + PT(NamedNode) data_root = new NamedNode("data_root"); + NamedNode *mouse = setup_mouse(data_root, window); + setup_trackball(mouse, camera); + + // Use an event handler to manage keyboard events. + EventHandler event_handler(EventQueue::get_global_event_queue()); + event_handler.add_hook("escape", event_esc); + event_handler.add_hook("q", event_esc); + // Put something in the scene graph to look at. get_models(render, argc, argv); - /* - // Put the scene a distance in front of our face so we can see it. - LMatrix4f mat = LMatrix4f::translate_mat(0, 20, 0); - TransformTransition *tt = new TransformTransition(mat); - render_arc->set_transition(tt); - */ - - // This is our main update loop. Run for 5 seconds, then shut down. + // This is our main update loop. Loop here until someone + // (e.g. event_esc) sets run_flag to false. ClockObject *clock = ClockObject::get_global_clock(); - clock->tick(); - double now = clock->get_frame_time(); - while (clock->get_frame_time() - now < 5.0) { + while (run_flag) { clock->tick(); + traverse_data_graph(data_root); + event_handler.process_events(); engine->render_frame(); } - cerr << "Exiting.\n"; delete engine; return (0); } diff --git a/panda/src/tform/Sources.pp b/panda/src/tform/Sources.pp index e403fc45c5..62ce07e179 100644 --- a/panda/src/tform/Sources.pp +++ b/panda/src/tform/Sources.pp @@ -4,7 +4,7 @@ #begin lib_target #define TARGET tform #define LOCAL_LIBS \ - dgraph graph linmath sgattrib display event putil gobj gsgbase \ + dgraph pgraph graph linmath sgattrib display event putil gobj gsgbase \ mathutil sgraph device sgraphutil #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx diff --git a/panda/src/tform/transform2sg.cxx b/panda/src/tform/transform2sg.cxx index daa0b0ec68..53b5645d46 100644 --- a/panda/src/tform/transform2sg.cxx +++ b/panda/src/tform/transform2sg.cxx @@ -20,6 +20,7 @@ #include "nodeRelation.h" #include "transformTransition.h" +#include "transformState.h" #include "matrixDataTransition.h" #include "allTransitionsWrapper.h" @@ -36,6 +37,7 @@ TypeHandle Transform2SG::_transform_type; Transform2SG:: Transform2SG(const string &name) : DataNode(name) { _arc = NULL; + _node = NULL; } @@ -60,6 +62,27 @@ get_arc() const { return _arc; } +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::set_node +// Access: Public +// Description: Sets the node that this node will adjust. +//////////////////////////////////////////////////////////////////// +void Transform2SG:: +set_node(PandaNode *node) { + _node = node; +} + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::get_node +// Access: Public +// Description: Returns the node that this node will adjust, or NULL +// if the node has not yet been set. +//////////////////////////////////////////////////////////////////// +PandaNode *Transform2SG:: +get_node() const { + return _node; +} + //////////////////////////////////////////////////////////////////// // Function: Transform2SG::transmit_data @@ -70,9 +93,14 @@ void Transform2SG:: transmit_data(AllTransitionsWrapper &data) { const NodeTransition *transform = data.get_transition(_transform_type); - if (transform != (NodeTransition *)NULL && _arc != (NodeRelation *)NULL) { + if (transform != (NodeTransition *)NULL) { const LMatrix4f &mat = DCAST(MatrixDataTransition, transform)->get_value(); - _arc->set_transition(new TransformTransition(mat)); + if (_arc != (NodeRelation *)NULL) { + _arc->set_transition(new TransformTransition(mat)); + } + if (_node != (PandaNode *)NULL) { + _node->set_transform(TransformState::make_mat(mat)); + } } // Clear the data below us. diff --git a/panda/src/tform/transform2sg.h b/panda/src/tform/transform2sg.h index c37ecd4bf3..fdeef9c119 100644 --- a/panda/src/tform/transform2sg.h +++ b/panda/src/tform/transform2sg.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "dataNode.h" +#include "pandaNode.h" class NodeRelation; @@ -39,8 +40,12 @@ PUBLISHED: void set_arc(NodeRelation *arc); NodeRelation *get_arc() const; + void set_node(PandaNode *node); + PandaNode *get_node() const; + private: NodeRelation *_arc; + PandaNode *_node; //////////////////////////////////////////////////////////////////// // From parent class DataNode