Browse Source

RigidBodyCombiner

David Rose 19 years ago
parent
commit
33e3ae85ab

+ 1 - 1
panda/src/gobj/userVertexTransform.cxx

@@ -36,7 +36,7 @@ UserVertexTransform(const string &name) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: UserVertexTransform::get_matrix
 //     Function: UserVertexTransform::get_matrix
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Stores the transform's matrix in the indicated object.
+//  Description: Returns the transform's matrix.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void UserVertexTransform::
 void UserVertexTransform::
 get_matrix(LMatrix4f &matrix) const {
 get_matrix(LMatrix4f &matrix) const {

+ 12 - 5
panda/src/grutil/Sources.pp

@@ -12,34 +12,41 @@
 
 
   #define SOURCES \
   #define SOURCES \
     cardMaker.I cardMaker.h \
     cardMaker.I cardMaker.h \
-    fisheyeMaker.I fisheyeMaker.h \
     config_grutil.h \
     config_grutil.h \
+    ffmpegTexture.I ffmpegTexture.h \
+    fisheyeMaker.I fisheyeMaker.h \
     frameRateMeter.I frameRateMeter.h \
     frameRateMeter.I frameRateMeter.h \
     heightfieldTesselator.I heightfieldTesselator.h \
     heightfieldTesselator.I heightfieldTesselator.h \
     lineSegs.I lineSegs.h \
     lineSegs.I lineSegs.h \
     multitexReducer.I multitexReducer.h multitexReducer.cxx \
     multitexReducer.I multitexReducer.h multitexReducer.cxx \
-    openCVTexture.h openCVTexture.I ffmpegTexture.h ffmpegTexture.I 
+    nodeVertexTransform.I nodeVertexTransform.h \
+    openCVTexture.I openCVTexture.h \
+    rigidBodyCombiner.I rigidBodyCombiner.h
     
     
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
     cardMaker.cxx \
     cardMaker.cxx \
+    ffmpegTexture.cxx \
     fisheyeMaker.cxx \
     fisheyeMaker.cxx \
     config_grutil.cxx \
     config_grutil.cxx \
     frameRateMeter.cxx \
     frameRateMeter.cxx \
     heightfieldTesselator.cxx \
     heightfieldTesselator.cxx \
+    nodeVertexTransform.cxx \
     openCVTexture.cxx \    	 
     openCVTexture.cxx \    	 
     lineSegs.cxx \
     lineSegs.cxx \
-    ffmpegTexture.cxx
+    rigidBodyCombiner.cxx
     
     
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     cardMaker.I cardMaker.h \
     cardMaker.I cardMaker.h \
+    ffmpegTexture.I ffmpegTexture.h \
     fisheyeMaker.I fisheyeMaker.h \
     fisheyeMaker.I fisheyeMaker.h \
     frameRateMeter.I frameRateMeter.h \
     frameRateMeter.I frameRateMeter.h \
     heightfieldTesselator.I heightfieldTesselator.h \
     heightfieldTesselator.I heightfieldTesselator.h \
     lineSegs.I lineSegs.h \
     lineSegs.I lineSegs.h \
     multitexReducer.I multitexReducer.h \
     multitexReducer.I multitexReducer.h \
-    openCVTexture.I openCVTexture.h\
-    ffmpegTexture.I ffmpegTexture.h
+    nodeVertexTransform.I nodeVertexTransform.h \
+    openCVTexture.I openCVTexture.h \
+    rigidBodyCombiner.I rigidBodyCombiner.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 5 - 0
panda/src/grutil/config_grutil.cxx

@@ -22,6 +22,8 @@
 #include "ffmpegTexture.h"
 #include "ffmpegTexture.h"
 #include "pandaSystem.h"
 #include "pandaSystem.h"
 #include "texturePool.h"
 #include "texturePool.h"
+#include "nodeVertexTransform.h"
+#include "rigidBodyCombiner.h"
 
 
 #include "dconfig.h"
 #include "dconfig.h"
 
 
@@ -65,6 +67,9 @@ init_libgrutil() {
   initialized = true;
   initialized = true;
 
 
   FrameRateMeter::init_type();
   FrameRateMeter::init_type();
+  NodeVertexTransform::init_type();
+  RigidBodyCombiner::init_type();
+
 #ifdef HAVE_OPENCV
 #ifdef HAVE_OPENCV
   
   
   {
   {

+ 2 - 0
panda/src/grutil/grutil_composite1.cxx

@@ -6,4 +6,6 @@
 #include "frameRateMeter.cxx"
 #include "frameRateMeter.cxx"
 #include "openCVTexture.cxx"
 #include "openCVTexture.cxx"
 #include "ffmpegTexture.cxx"
 #include "ffmpegTexture.cxx"
+#include "nodeVertexTransform.cxx"
+#include "rigidBodyCombiner.cxx"
 
 

+ 40 - 0
panda/src/grutil/nodeVertexTransform.I

@@ -0,0 +1,40 @@
+// Filename: nodeVertexTransform.I
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeVertexTransform::get_node
+//       Access: Published
+//  Description: Returns the PandaNode whose transform supplies this
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE const PandaNode *NodeVertexTransform::
+get_node() const {
+  return _node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeVertexTransform::get_prev
+//       Access: Published
+//  Description: Returns the VertexTransform object whose matrix will
+//               be composed with the result of this node's transform.
+////////////////////////////////////////////////////////////////////
+INLINE const VertexTransform *NodeVertexTransform::
+get_prev() const {
+  return _prev;
+}

+ 68 - 0
panda/src/grutil/nodeVertexTransform.cxx

@@ -0,0 +1,68 @@
+// Filename: nodeVertexTransform.cxx
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "nodeVertexTransform.h"
+
+TypeHandle NodeVertexTransform::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeVertexTransform::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+NodeVertexTransform::
+NodeVertexTransform(const PandaNode *node, 
+                    const VertexTransform *prev) :
+  _node(node),
+  _prev(prev)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeVertexTransform::get_matrix
+//       Access: Published, Virtual
+//  Description: Returns the transform of the associated node,
+//               composed with the previous VertexTransform if any,
+//               expressed as a matrix.
+////////////////////////////////////////////////////////////////////
+void NodeVertexTransform::
+get_matrix(LMatrix4f &matrix) const {
+  if (_prev != (const VertexTransform *)NULL) {
+    LMatrix4f prev_matrix;
+    _prev->get_matrix(prev_matrix);
+    matrix.multiply(_node->get_transform()->get_mat(), prev_matrix);
+
+  } else {
+    matrix = _node->get_transform()->get_mat();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeVertexTransform::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NodeVertexTransform::
+output(ostream &out) const {
+  if (_prev != (const VertexTransform *)NULL) {
+    _prev->output(out);
+    out << " * ";
+  }
+
+  out << "NodeVertexTransform(" << _node->get_name() << ")";
+}

+ 74 - 0
panda/src/grutil/nodeVertexTransform.h

@@ -0,0 +1,74 @@
+// Filename: nodeVertexTransform.h
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef NODEVERTEXTRANSFORM_H
+#define NODEVERTEXTRANSFORM_H
+
+#include "pandabase.h"
+#include "vertexTransform.h"
+
+class FactoryParams;
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodeVertexTransform
+// Description : This VertexTransform gets its matrix from the
+//               Transform stored on a node.  It can also compose its
+//               node's transform with another VertexTransform,
+//               allowing you to build up a chain of
+//               NodeVertexTransforms that represent a list of
+//               composed matrices.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA NodeVertexTransform : public VertexTransform {
+PUBLISHED:
+  NodeVertexTransform(const PandaNode *node, 
+                      const VertexTransform *prev = NULL);
+
+  INLINE const PandaNode *get_node() const;
+  INLINE const VertexTransform *get_prev() const;
+
+  virtual void get_matrix(LMatrix4f &matrix) const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  CPT(PandaNode) _node;
+  CPT(VertexTransform) _prev;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    VertexTransform::init_type();
+    register_type(_type_handle, "NodeVertexTransform",
+                  VertexTransform::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 RigidBodyCombiner;
+};
+
+#include "nodeVertexTransform.I"
+
+#endif

+ 18 - 0
panda/src/grutil/rigidBodyCombiner.I

@@ -0,0 +1,18 @@
+// Filename: rigidBodyCombiner.I
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 258 - 0
panda/src/grutil/rigidBodyCombiner.cxx

@@ -0,0 +1,258 @@
+// Filename: rigidBodyCombiner.cxx
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "rigidBodyCombiner.h"
+#include "nodePath.h"
+#include "geomNode.h"
+#include "modelNode.h"
+#include "geomVertexData.h"
+#include "geomVertexFormat.h"
+#include "geomVertexArrayFormat.h"
+#include "geomVertexAnimationSpec.h"
+#include "sceneGraphReducer.h"
+
+TypeHandle RigidBodyCombiner::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+RigidBodyCombiner::
+RigidBodyCombiner(const string &name) : PandaNode(name) {
+  set_cull_callback();
+
+  _internal_root = new PandaNode(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::Copy Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+RigidBodyCombiner::
+RigidBodyCombiner(const RigidBodyCombiner &copy) : PandaNode(copy) {
+  set_cull_callback();
+
+  _internal_root = copy._internal_root;
+  _internal_transforms = copy._internal_transforms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *RigidBodyCombiner::
+make_copy() const {
+  return new RigidBodyCombiner(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::collect
+//       Access: Published
+//  Description: Walks through the entire subgraph of nodes rooted at
+//               this node, accumulates all of the RenderAttribs and
+//               Geoms below this node, flattening them into just one
+//               Geom (or as few as possible, if there are multiple
+//               different states).
+//
+//               Nodes that have transforms on them at the time of
+//               collect(), or any ModelNodes with the
+//               preserve_transform flag, will be identified as
+//               "moving" nodes, and their transforms will be
+//               monitored as they change in future frames and each
+//               new transform directly applied to the vertices.
+//               
+//               This call must be made after adding any nodes to or
+//               removing any nodes from the subgraph rooted at this
+//               node.  It should not be made too often, as it is a
+//               relatively expensive call.  If you need to hide
+//               children of this node, consider scaling them to zero
+//               (or very near zero), or moving them behind the
+//               camera, instead.
+////////////////////////////////////////////////////////////////////
+void RigidBodyCombiner::
+collect() {
+  _internal_root = new GeomNode(get_name());
+  _internal_transforms.clear();
+
+  Children cr = get_children();
+  int num_children = cr.get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    r_collect(cr.get_child(i), RenderState::make_empty(), NULL);
+  }
+
+  SceneGraphReducer gr;
+  gr.apply_attribs(_internal_root);
+  gr.collect_vertex_data(_internal_root, ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type));
+  gr.unify(_internal_root);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::get_internal_scene
+//       Access: Published
+//  Description: Returns a special NodePath that represents the
+//               internal node of this object.  This is the node that
+//               is actually sent to the graphics card for rendering;
+//               it contains the collection of the children of this
+//               node into as few Geoms as possible.  
+//
+//               This node is filled up by the last call to collect().
+////////////////////////////////////////////////////////////////////
+NodePath RigidBodyCombiner::
+get_internal_scene() {
+  return NodePath(_internal_root);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::cull_callback
+//       Access: Protected, Virtual
+//  Description: This function will be called during the cull
+//               traversal to perform any additional operations that
+//               should be performed at cull time.  This may include
+//               additional manipulation of render state or additional
+//               visible/invisible decisions, or any other arbitrary
+//               operation.
+//
+//               Note that this function will *not* be called unless
+//               set_cull_callback() is called in the constructor of
+//               the derived class.  It is necessary to call
+//               set_cull_callback() to indicated that we require
+//               cull_callback() to be called.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool RigidBodyCombiner::
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  // Pretend that all of our transforms have been modified (since we
+  // don't really know which ones have).
+  Thread *current_thread = Thread::get_current_thread();
+  Transforms::iterator ti;
+  for (ti = _internal_transforms.begin(); 
+       ti != _internal_transforms.end(); 
+       ++ti) {
+    (*ti)->mark_modified(current_thread);
+  }
+
+  // Render the internal scene only--this is the optimized scene.
+  CullTraverserData next_data(data, _internal_root);
+  trav->traverse(next_data);
+
+  // Do not directly render the nodes beneath this node.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::r_collect
+//       Access: Private
+//  Description: Recursively visits each child or descedant of this
+//               node, accumulating state and transform as we go.
+//               When GeomNodes are encountered, their Geoms are
+//               extracted and added to the _internal_root node.
+////////////////////////////////////////////////////////////////////
+void RigidBodyCombiner::
+r_collect(PandaNode *node, const RenderState *state, 
+          const VertexTransform *transform) {
+  CPT(RenderState) next_state = state->compose(node->get_state());
+  CPT(VertexTransform) next_transform = transform;
+  if (!node->get_transform()->is_identity() ||
+      node->is_of_type(ModelNode::get_class_type()) &&
+      DCAST(ModelNode, node)->get_preserve_transform() != ModelNode::PT_none) {
+    // This node has a transform we need to keep.
+    PT(NodeVertexTransform) new_transform = new NodeVertexTransform(node, transform);
+    _internal_transforms.push_back(new_transform);
+    next_transform = new_transform.p();
+    
+  }
+
+  if (node->is_geom_node()) {
+    GeomNode *gnode = DCAST(GeomNode, node);
+    GeomNode *root_gnode = DCAST(GeomNode, _internal_root);
+
+    int num_geoms = gnode->get_num_geoms();
+    for (int i = 0; i < num_geoms; ++i) {
+      PT(Geom) geom = gnode->get_geom(i)->make_copy();
+      if (transform != (const VertexTransform *)NULL) {
+        geom->set_vertex_data(convert_vd(transform, geom->get_vertex_data()));
+      }
+      CPT(RenderState) gstate = next_state->compose(gnode->get_geom_state(i));
+      root_gnode->add_geom(geom, gstate);
+    }
+  }
+
+  Children cr = node->get_children();
+  int num_children = cr.get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    r_collect(cr.get_child(i), next_state, next_transform);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RigidBodyCombiner::convert_vd
+//       Access: Private
+//  Description: Converts a GeomVertexData to a new form in which all
+//               of the vertices are transformed by the node's
+//               transform.
+////////////////////////////////////////////////////////////////////
+PT(GeomVertexData) RigidBodyCombiner::
+convert_vd(const VertexTransform *transform, const GeomVertexData *orig) {
+  // TODO: unify this operation for unique transform/data combinations.
+
+  PT(GeomVertexFormat) format = new GeomVertexFormat(*orig->get_format());
+  if (!orig->get_format()->has_column(InternalName::get_transform_blend())) {
+    PT(GeomVertexArrayFormat) af = 
+      new GeomVertexArrayFormat(InternalName::get_transform_blend(), 1, 
+                                Geom::NT_uint16, Geom::C_index);
+    format->add_array(af);
+  }
+
+  GeomVertexAnimationSpec spec;
+  spec.set_panda();
+  format->set_animation(spec);
+
+  CPT(GeomVertexFormat) new_format = GeomVertexFormat::register_format(format);
+  CPT(GeomVertexData) converted = orig->convert_to(new_format);
+  PT(GeomVertexData) new_data = new GeomVertexData(*converted);
+  
+  if (new_data->get_transform_blend_table() == (TransformBlendTable *)NULL) {
+    // Create a new table that has just the one blend: all vertices
+    // hard-assigned to the indicated transform.
+    PT(TransformBlendTable) new_table = new TransformBlendTable;
+    new_table->add_blend(TransformBlend(transform, 1.0f));
+    new_table->set_rows(SparseArray::range(0, new_data->get_num_rows()));
+    new_data->set_transform_blend_table(new_table);
+  } else {
+    // The GeomVertexData already has a TransformBlendTable.  In this
+    // case, we'll have to adjust it.  TODO.
+  }
+
+  return new_data;
+}

+ 103 - 0
panda/src/grutil/rigidBodyCombiner.h

@@ -0,0 +1,103 @@
+// Filename: rigidBodyCombiner.h
+// Created by:  drose (22Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef RIGIDBODYCOMBINER_H
+#define RIGIDBODYCOMBINER_H
+
+#include "pandabase.h"
+
+#include "pandaNode.h"
+#include "nodeVertexTransform.h"
+#include "pvector.h"
+
+class NodePath;
+
+////////////////////////////////////////////////////////////////////
+//       Class : RigidBodyCombiner
+// Description : This is a special node that combines multiple
+//               independently-moving rigid nodes into one Geom
+//               internally (or as few Geoms as possible), for the
+//               purposes of improving rendering performance.
+//
+//               To use it, parent a number of moving objects to this
+//               node and call collect().  A child node is identified
+//               as "moving" if (a) it has a non-identity transform
+//               initially, or (b) it is a ModelNode with the
+//               preserve_transform flag set.  Any other nodes will be
+//               considered static, and later transforms applied to
+//               them will not be identified.
+//
+//               You should call collect() only at startup or if you
+//               change the set of children; it is a relatively
+//               expensive call.
+//
+//               Once you call collect(), you may change the
+//               transforms on the child nodes freely without having
+//               to call collect() again.
+//
+//               RenderEffects such as Billboards are not supported
+//               below this node.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA RigidBodyCombiner : public PandaNode {
+PUBLISHED:
+  RigidBodyCombiner(const string &name);
+protected:
+  RigidBodyCombiner(const RigidBodyCombiner &copy);
+  virtual PandaNode *make_copy() const;
+
+PUBLISHED:
+  void collect();
+
+  NodePath get_internal_scene();
+
+public:
+  // From parent class PandaNode
+  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+
+private:
+  void r_collect(PandaNode *node, const RenderState *state, 
+                 const VertexTransform *transform);
+  PT(GeomVertexData) convert_vd(const VertexTransform *transform, 
+                                const GeomVertexData *orig);
+
+  PT(PandaNode) _internal_root;
+
+  typedef pvector< PT(NodeVertexTransform) > Transforms;
+  Transforms _internal_transforms;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "RigidBodyCombiner",
+                  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 "rigidBodyCombiner.I"
+
+#endif