Browse Source

more refinements

David Rose 24 years ago
parent
commit
1ac667d4c6

+ 13 - 5
panda/src/pgraph/Sources.pp

@@ -8,32 +8,40 @@
   #define SOURCES \
     colorAttrib.h colorAttrib.I \
     config_pgraph.h \
+    cullHandler.h \
+    qpcullTraverser.h qpcullTraverser.I \
     cycleData.h cycleData.I \
     cycleDataReader.h cycleDataReader.I \
     cycleDataWriter.h cycleDataWriter.I \
+    qpgeomNode.h qpgeomNode.I \
     pandaNode.h pandaNode.I \
     pipeline.h pipeline.I \
     pipelineCycler.h pipelineCycler.I \
     pipelineCyclerBase.h pipelineCyclerBase.I \
     renderAttrib.h renderAttrib.I \
     renderState.h renderState.I \
-    textureAttrib.h textureAttrib.I
+    textureAttrib.h textureAttrib.I \
+    transformAttrib.h transformAttrib.I
     
-  #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
-//  #define SOURCES $[SOURCES] \
-  #define INCLUDED_SOURCES \
+//  #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
+//  #define INCLUDED_SOURCES \
+  #define SOURCES $[SOURCES] \
     colorAttrib.cxx \
     config_pgraph.cxx \
+    cullHandler.cxx \
+    qpcullTraverser.cxx \
     cycleData.cxx \
     cycleDataReader.cxx \
     cycleDataWriter.cxx \
+    qpgeomNode.cxx \
     pandaNode.cxx \
     pipeline.cxx \
     pipelineCycler.cxx \
     pipelineCyclerBase.cxx \
     renderAttrib.cxx \
     renderState.cxx \
-    textureAttrib.cxx
+    textureAttrib.cxx \
+    transformAttrib.cxx
 
   #define INSTALL_HEADERS \
     pandaNode.h pandaNode.I

+ 61 - 3
panda/src/pgraph/colorAttrib.cxx

@@ -18,6 +18,10 @@
 
 #include "colorAttrib.h"
 #include "dcast.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
 
 TypeHandle ColorAttrib::_type_handle;
 
@@ -30,7 +34,7 @@ TypeHandle ColorAttrib::_type_handle;
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ColorAttrib::
 make_vertex() {
-  ColorAttrib *attrib = new ColorAttrib(T_vertex, Colorf(0.0f, 0.0f, 0.0f, 1.0f));
+  ColorAttrib *attrib = new ColorAttrib(T_vertex);
   return return_new(attrib);
 }
 
@@ -55,7 +59,7 @@ make_flat(const Colorf &color) {
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ColorAttrib::
 make_off() {
-  ColorAttrib *attrib = new ColorAttrib(T_off, Colorf(0.0f, 0.0f, 0.0f, 1.0f));
+  ColorAttrib *attrib = new ColorAttrib(T_off);
   return return_new(attrib);
 }
 
@@ -123,5 +127,59 @@ compare_to_impl(const RenderAttrib *other) const {
 ////////////////////////////////////////////////////////////////////
 RenderAttrib *ColorAttrib::
 make_default_impl() const {
-  return new ColorAttrib(T_vertex, Colorf(0.0f, 0.0f, 0.0f, 1.0f));
+  return new ColorAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ColorAttrib.
+////////////////////////////////////////////////////////////////////
+void ColorAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ColorAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ColorAttrib is encountered
+//               in the Bam file.  It should create the ColorAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ColorAttrib::
+make_from_bam(const FactoryParams &params) {
+  ColorAttrib *attrib = new ColorAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return new_from_bam(attrib, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorAttrib::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 ColorAttrib.
+////////////////////////////////////////////////////////////////////
+void ColorAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
 }

+ 12 - 3
panda/src/pgraph/colorAttrib.h

@@ -36,7 +36,8 @@ PUBLISHED:
   };
 
 private:
-  INLINE ColorAttrib(Type type, const Colorf &color);
+  INLINE ColorAttrib(Type type = T_vertex, 
+                     const Colorf &color = Colorf(0.0f, 0.0f, 0.0f, 1.0f));
 
 PUBLISHED:
   static CPT(RenderAttrib) make_vertex();
@@ -56,15 +57,23 @@ protected:
 private:
   Type _type;
   Colorf _color;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    RenderAttrib::init_type();
     register_type(_type_handle, "ColorAttrib",
-                  TypedWritableReferenceCount::get_class_type());
+                  RenderAttrib::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 8 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -19,10 +19,12 @@
 #include "config_pgraph.h"
 
 #include "colorAttrib.h"
+#include "qpgeomNode.h"
 #include "pandaNode.h"
 #include "renderAttrib.h"
 #include "renderState.h"
 #include "textureAttrib.h"
+#include "transformAttrib.h"
 
 #include "dconfig.h"
 
@@ -51,11 +53,17 @@ init_libpgraph() {
   initialized = true;
 
   ColorAttrib::init_type();
+  qpGeomNode::init_type();
   PandaNode::init_type();
   RenderAttrib::init_type();
   RenderState::init_type();
   TextureAttrib::init_type();
+  TransformAttrib::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();
 }

+ 36 - 0
panda/src/pgraph/cullHandler.cxx

@@ -0,0 +1,36 @@
+// Filename: cullHandler.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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cullHandler.h"
+#include "geom.h"
+#include "renderState.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullHandler::found_geom
+//       Access: Public, Virtual
+//  Description: This callback function is intended to be overridden
+//               by a derived class.  This is called as each Geom is
+//               discovered by the CullTraverser.
+//
+//               This default method simply outputs a message to cerr;
+//               it's not intended to be used except for debugging.
+////////////////////////////////////////////////////////////////////
+void CullHandler::
+found_geom(Geom *geom, const RenderState *state) {
+  cerr << *geom << " " << *state << "\n";
+}

+ 42 - 0
panda/src/pgraph/cullHandler.h

@@ -0,0 +1,42 @@
+// Filename: cullHandler.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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CULLHANDLER_H
+#define CULLHANDLER_H
+
+#include "pandabase.h"
+
+class Geom;
+class RenderState;
+
+////////////////////////////////////////////////////////////////////
+//       Class : CullHandler
+// Description : This defines the abstract interface for an object
+//               that receives Geoms identified by the CullTraverser.
+//               By itself, it's not a particularly useful class; to
+//               use it, derive from it and redefine found_geom().
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CullHandler {
+public:
+  virtual void found_geom(Geom *geom, const RenderState *state);
+};
+
+#endif
+
+
+  

+ 0 - 3
panda/src/pgraph/cycleDataReader.h

@@ -51,9 +51,6 @@ private:
   const CycleDataType *_pointer;
 };
 
-// This abbreviation macro is used for ease of typing.
-#define CDR(type) CycleDataReader< type >
-
 #include "cycleDataReader.I"
 
 #endif

+ 0 - 2
panda/src/pgraph/cycleDataWriter.h

@@ -52,8 +52,6 @@ private:
   CycleDataType *_pointer;
 };
 
-#define CDW(type) CycleDataWriter< type >
-
 #include "cycleDataWriter.I"
 
 #endif

+ 16 - 26
panda/src/pgraph/pandaNode.I

@@ -78,8 +78,8 @@ CData() {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Children::
-Children(const PipelineCycler<PandaNode::CData> &cycler) :
-  _cdata(cycler)
+Children(const PandaNode::CDReader &cdata) :
+  _cdata(cdata)
 {
 }
 
@@ -115,17 +115,6 @@ get_child(int n) const {
   return _cdata->_down[n].get_child();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::Constructor
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::
-PandaNode(const string &name) :
-  Namable(name)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_num_parents
 //       Access: Published
@@ -137,7 +126,7 @@ PandaNode(const string &name) :
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 get_num_parents() const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   return cdata->_up.size();
 }
 
@@ -149,7 +138,7 @@ get_num_parents() const {
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNode::
 get_parent(int n) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   nassertr(n >= 0 && n < (int)cdata->_up.size(), NULL);
   return cdata->_up[n];
 }
@@ -162,7 +151,7 @@ get_parent(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 find_parent(PandaNode *node) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   Up::const_iterator ui = cdata->_up.find(node);
   if (ui == cdata->_up.end()) {
     return -1;
@@ -180,7 +169,7 @@ find_parent(PandaNode *node) const {
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 get_num_children() const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   return cdata->_down.size();
 }
 
@@ -192,7 +181,7 @@ get_num_children() const {
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNode::
 get_child(int n) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   nassertr(n >= 0 && n < (int)cdata->_down.size(), NULL);
   return cdata->_down[n].get_child();
 }
@@ -206,7 +195,7 @@ get_child(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 get_sort(int n) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   nassertr(n >= 0 && n < (int)cdata->_down.size(), -1);
   return cdata->_down[n].get_sort();
 }
@@ -222,7 +211,7 @@ get_sort(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 set_attrib(const RenderAttrib *attrib, int override) {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   cdata->_state_changes = cdata->_state_changes->add(attrib, override);
 }
 
@@ -237,7 +226,7 @@ set_attrib(const RenderAttrib *attrib, int override) {
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderAttrib *PandaNode::
 get_attrib(TypeHandle type) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   int index = cdata->_state_changes->find_attrib(type);
   if (index >= 0) {
     return cdata->_state_changes->get_attrib(index);
@@ -255,7 +244,7 @@ get_attrib(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 clear_attrib(TypeHandle type) {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   cdata->_state_changes = cdata->_state_changes->remove(type);
 }
 
@@ -271,7 +260,7 @@ clear_attrib(TypeHandle type) {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 set_state(const RenderState *state) {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   cdata->_state_changes = state;
 }
 
@@ -286,7 +275,7 @@ set_state(const RenderState *state) {
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderState *PandaNode::
 get_state() const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   return cdata->_state_changes;
 }
 
@@ -300,7 +289,7 @@ get_state() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 clear_state() {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   cdata->_state_changes = RenderState::make_empty();
 }
 
@@ -320,6 +309,7 @@ clear_state() {
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Children PandaNode::
 get_children() const {
-  return Children(_cycler);
+  CDReader cdata(_cycler);
+  return Children(cdata);
 }
 

+ 42 - 15
panda/src/pgraph/pandaNode.cxx

@@ -50,6 +50,17 @@ make_copy() const {
   return new CData(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+PandaNode::
+PandaNode(const string &name) :
+  Namable(name)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::Copy Constructor
 //       Access: Published
@@ -64,8 +75,8 @@ PandaNode(const PandaNode &copy) :
   // Copying a node does not copy its children.
 
   // Copy the other node's bounding volume.
-  CDR(CData) copy_cdata(copy._cycler);
-  CDW(CData) cdata(_cycler);
+  CDReader copy_cdata(copy._cycler);
+  CDWriter cdata(_cycler);
   cdata->_node_bounds = copy_cdata->_node_bounds;
 }
 
@@ -81,8 +92,8 @@ operator = (const PandaNode &copy) {
   ReferenceCount::operator = (copy);
 
   // Copy the other node's bounding volume.
-  CDR(CData) copy_cdata(copy._cycler);
-  CDW(CData) cdata(_cycler);
+  CDReader copy_cdata(copy._cycler);
+  CDWriter cdata(_cycler);
   cdata->_node_bounds = copy_cdata->_node_bounds;
 }
 
@@ -95,7 +106,7 @@ PandaNode::
 ~PandaNode() {
   // We shouldn't have any parents left by the time we destruct, or
   // there's a refcount fault somewhere.
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   nassertv(cdata->_up.empty());
 
   remove_all_children();
@@ -109,7 +120,7 @@ PandaNode::
 ////////////////////////////////////////////////////////////////////
 int PandaNode::
 find_child(PandaNode *node) const {
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
 
   // We have to search for the child by brute force, since we don't
   // know what sort index it was added as.
@@ -139,8 +150,8 @@ find_child(PandaNode *node) const {
 int PandaNode::
 add_child(PandaNode *child, int sort) {
   remove_child(child);
-  CDW(CData) cdata(_cycler);
-  CDW(CData) cdata_child(child->_cycler);
+  CDWriter cdata(_cycler);
+  CDWriter cdata_child(child->_cycler);
 
   Down::iterator ci = cdata->_down.insert(DownConnection(child, sort));
   cdata_child->_up.insert(this);
@@ -155,11 +166,11 @@ add_child(PandaNode *child, int sort) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 remove_child(int n) {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   nassertv(n >= 0 && n < (int)cdata->_down.size());
 
   PandaNode *child = cdata->_down[n].get_child();
-  CDW(CData) cdata_child(child->_cycler);
+  CDWriter cdata_child(child->_cycler);
 
   cdata->_down.erase(cdata->_down.begin() + n);
   int num_erased = cdata_child->_up.erase(this);
@@ -175,7 +186,7 @@ remove_child(int n) {
 ////////////////////////////////////////////////////////////////////
 bool PandaNode::
 remove_child(PandaNode *child) {
-  CDW(CData) cdata_child(child->_cycler);
+  CDWriter cdata_child(child->_cycler);
 
   // First, look for and remove this node from the child's parent
   // list.
@@ -185,7 +196,7 @@ remove_child(PandaNode *child) {
     return false;
   }
 
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
 
   // Now, look for and remove the child node from our down list.
   Down::iterator ci;
@@ -208,11 +219,11 @@ remove_child(PandaNode *child) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 remove_all_children() {
-  CDW(CData) cdata(_cycler);
+  CDWriter cdata(_cycler);
   Down::iterator ci;
   for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
     PandaNode *child = (*ci).get_child();
-    CDW(CData) child_cdata(child->_cycler);
+    CDWriter child_cdata(child->_cycler);
     child_cdata->_up.erase(this);
   }
 }
@@ -235,13 +246,29 @@ output(ostream &out) const {
 void PandaNode::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this;
-  CDR(CData) cdata(_cycler);
+  CDReader cdata(_cycler);
   if (!cdata->_state_changes->is_empty()) {
     out << " (" << *cdata->_state_changes << ")";
   }
   out << "\n";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::is_geom_node
+//       Access: Public, Virtual
+//  Description: A simple downcast check.  Returns true if this kind
+//               of node happens to inherit from GeomNode, false
+//               otherwise.
+//
+//               This is provided as a a faster alternative to calling
+//               is_of_type(GeomNode::get_class_type()), since this
+//               test is so important to rendering.
+////////////////////////////////////////////////////////////////////
+bool PandaNode::
+is_geom_node() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::register_with_read_factory
 //       Access: Public, Static

+ 10 - 5
panda/src/pgraph/pandaNode.h

@@ -41,10 +41,10 @@
 //               is the base class of all specialized nodes, and also
 //               serves as a generic node with no special properties.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA PandaNode : public TypedWritable, 
-                              public Namable, virtual public ReferenceCount {
+class EXPCL_PANDA PandaNode : public TypedWritable, public Namable,
+                              virtual public ReferenceCount {
 PUBLISHED:
-  INLINE PandaNode(const string &name);
+  PandaNode(const string &name);
   PandaNode(const PandaNode &copy);
   void operator = (const PandaNode &copy);
   virtual ~PandaNode();
@@ -75,6 +75,9 @@ PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
 
+public:
+  virtual bool is_geom_node() const;
+
 private:
   class EXPCL_PANDA DownConnection {
   public:
@@ -114,6 +117,8 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
 
 public:
   // Use this interface when you want to walk through the list of
@@ -122,14 +127,14 @@ public:
   // However, it does not protect you from self-modifying loops.
   class EXPCL_PANDA Children {
   public:
-    INLINE Children(const PipelineCycler<CData> &cycler);
+    INLINE Children(const CDReader &cdata);
     INLINE Children(const Children &copy);
 
     INLINE int get_num_children() const;
     INLINE PandaNode *get_child(int n) const;
 
   private:
-    CDR(CData) _cdata;
+    CDReader _cdata;
   };
 
   INLINE Children get_children() const;

+ 9 - 6
panda/src/pgraph/pgraph_composite1.cxx

@@ -1,6 +1,9 @@
-#include "colorAttrib.cxx"
-#include "config_pgraph.cxx"
-#include "cycleData.cxx"
-#include "cycleDataReader.cxx"
-#include "cycleDataWriter.cxx"
-#include "pandaNode.cxx"
+#include "colorAttrib.cxx"
+#include "config_pgraph.cxx"
+#include "cullHandler.cxx"
+#include "qpcullTraverser.cxx"
+#include "cycleData.cxx"
+#include "cycleDataReader.cxx"
+#include "cycleDataWriter.cxx"
+#include "qpgeomNode.cxx"
+#include "pandaNode.cxx"

+ 9 - 8
panda/src/pgraph/pgraph_composite2.cxx

@@ -1,8 +1,9 @@
-#include "pipeline.cxx"
-#include "pipelineCycler.cxx"
-#include "pipelineCyclerBase.cxx"
-#include "renderAttrib.cxx"
-#include "renderState.cxx"
-#include "test_pgraph.cxx"
-#include "textureAttrib.cxx"
-
+#include "pipeline.cxx"
+#include "pipelineCycler.cxx"
+#include "pipelineCyclerBase.cxx"
+#include "renderAttrib.cxx"
+#include "renderState.cxx"
+#include "test_pgraph.cxx"
+#include "textureAttrib.cxx"
+#include "transformAttrib.cxx"
+

+ 17 - 0
panda/src/pgraph/qpcullTraverser.I

@@ -0,0 +1,17 @@
+// Filename: qpcullTraverser.I
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////

+ 117 - 0
panda/src/pgraph/qpcullTraverser.cxx

@@ -0,0 +1,117 @@
+// Filename: qpcullTraverser.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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpcullTraverser.h"
+#include "renderState.h"
+#include "cullHandler.h"
+#include "dcast.h"
+#include "qpgeomNode.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpCullTraverser::
+qpCullTraverser() {
+  _initial_state = RenderState::make_empty();
+  _world_transform = DCAST(TransformAttrib, TransformAttrib::make_identity());
+  _cull_handler = (CullHandler *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_initial_state
+//       Access: Public
+//  Description: Sets the initial RenderState at the top of the scene
+//               graph we are traversing.  If this is not set, the
+//               default is the empty state.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_initial_state(const RenderState *initial_state) {
+  _initial_state = 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.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_world_transform(const TransformAttrib *world_transform) {
+  _world_transform = 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().
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_cull_handler(CullHandler *cull_handler) {
+  _cull_handler = cull_handler;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::traverse
+//       Access: Public
+//  Description: Begins the traversal from the indicated node.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+traverse(PandaNode *root) {
+  nassertv(_cull_handler != (CullHandler *)NULL);
+
+  r_traverse(root, _initial_state, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::r_traverse
+//       Access: Private
+//  Description: The recursive traversal implementation.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+r_traverse(PandaNode *node, const RenderState *state, int flags) {
+  CPT(RenderState) next_state = state->compose(node->get_state());
+
+  if (node->is_geom_node()) {
+    qpGeomNode *geom_node;
+    DCAST_INTO_V(geom_node, node);
+    
+    int num_geoms = geom_node->get_num_geoms();
+    for (int i = 0; i < num_geoms; i++) {
+      Geom *geom = geom_node->get_geom(i);
+      CPT(RenderState) geom_state = 
+        next_state->compose(geom_node->get_geom_state(i));
+      _cull_handler->found_geom(geom, geom_state);
+    }
+  }
+
+  // Now visit all the node's children.
+  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);
+  }
+}

+ 62 - 0
panda/src/pgraph/qpcullTraverser.h

@@ -0,0 +1,62 @@
+// Filename: qpcullTraverser.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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpCULLTRAVERSER_H
+#define qpCULLTRAVERSER_H
+
+#include "pandabase.h"
+
+#include "renderState.h"
+#include "transformAttrib.h"
+#include "pointerTo.h"
+
+class PandaNode;
+class CullHandler;
+
+////////////////////////////////////////////////////////////////////
+//       Class : CullTraverser
+// Description : This object performs a depth-first traversal of the
+//               scene graph, with optional view-frustum culling,
+//               collecting CullState and searching for GeomNodes.
+//               Each renderable Geom encountered is passed along with
+//               its associated RenderState to the CullHandler object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpCullTraverser {
+public:
+  qpCullTraverser();
+
+  void set_initial_state(const RenderState *initial_state);
+  void set_world_transform(const TransformAttrib *world_transform);
+  void set_cull_handler(CullHandler *cull_handler);
+
+  void traverse(PandaNode *root);
+
+private:
+  void r_traverse(PandaNode *node, const RenderState *state, int flags);
+
+  CPT(RenderState) _initial_state;
+  CPT(TransformAttrib) _world_transform;
+  CullHandler *_cull_handler;
+};
+
+#include "qpcullTraverser.I"
+
+#endif
+
+
+  

+ 140 - 0
panda/src/pgraph/qpgeomNode.I

@@ -0,0 +1,140 @@
+// Filename: qpgeomNode.I
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::GeomEntry::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomNode::GeomEntry::
+GeomEntry(Geom *geom, const RenderState *state) :
+  _geom(geom),
+  _state(state)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomNode::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::get_num_geoms
+//       Access: Public
+//  Description: Returns the number of geoms in the node.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomNode::
+get_num_geoms() const {
+  CDReader cdata(_cycler);
+  return cdata->_geoms.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::get_geom
+//       Access: Public
+//  Description: Returns the nth geom of the node.
+////////////////////////////////////////////////////////////////////
+INLINE Geom *qpGeomNode::
+get_geom(int n) const {
+  CDReader cdata(_cycler);
+  nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
+  return cdata->_geoms[n]._geom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::get_geom_state
+//       Access: Public
+//  Description: Returns the RenderState associated with the nth geom
+//               of the node.  This is just the RenderState directly
+//               associated with the Geom; the actual state in which
+//               the Geom is rendered will also be affected by
+//               RenderStates that appear on the scene graph in nodes
+//               above this GeomNode.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *qpGeomNode::
+get_geom_state(int n) const {
+  CDReader cdata(_cycler);
+  nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
+  return cdata->_geoms[n]._state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::set_state
+//       Access: Public
+//  Description: Changes the RenderState associated with the nth geom
+//               of the node.  This is just the RenderState directly
+//               associated with the Geom; the actual state in which
+//               the Geom is rendered will also be affected by
+//               RenderStates that appear on the scene graph in nodes
+//               above this GeomNode.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomNode::
+set_state(int n, const RenderState *state) {
+  CDWriter cdata(_cycler);
+  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
+  cdata->_geoms[n]._state = state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::add_geom
+//       Access: Published
+//  Description: Adds a new Geom to the node.  The geom is given the
+//               indicated state (which may be
+//               RenderState::make_empty(), to completely inherit its
+//               state from the scene graph).
+//
+//               The return value is the index number of the new Geom.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomNode::
+add_geom(Geom *geom, const RenderState *state) {
+  nassertr(geom != (Geom *)NULL, -1);
+  nassertr(state != (RenderState *)NULL, -1);
+  CDWriter cdata(_cycler);
+
+  cdata->_geoms.push_back(GeomEntry(geom, state));
+  return cdata->_geoms.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::remove_geom
+//       Access: Published
+//  Description: Removes the nth geom from the node.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomNode::
+remove_geom(int n) {
+  CDWriter cdata(_cycler);
+  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
+
+  cdata->_geoms.erase(cdata->_geoms.begin() + n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::remove_all_geoms
+//       Access: Published
+//  Description: Removes all the geoms from the node at once.
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomNode::
+remove_all_geoms() {
+  CDWriter cdata(_cycler);
+  cdata->_geoms.clear();
+}

+ 214 - 0
panda/src/pgraph/qpgeomNode.cxx

@@ -0,0 +1,214 @@
+// Filename: qpgeomNode.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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpgeomNode.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "indent.h"
+
+TypeHandle qpGeomNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpGeomNode::CData::
+CData(const qpGeomNode::CData &copy) :
+  _geoms(copy._geoms)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *qpGeomNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpGeomNode::
+qpGeomNode(const string &name) :
+  PandaNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::Copy Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpGeomNode::
+qpGeomNode(const qpGeomNode &copy) :
+  PandaNode(copy)
+{
+  // Copy the other node's _geoms.
+  CDReader copy_cdata(copy._cycler);
+  CDWriter cdata(_cycler);
+  cdata->_geoms = copy_cdata->_geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::Copy Assignment Operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+operator = (const qpGeomNode &copy) {
+  PandaNode::operator = (copy);
+
+  // Copy the other node's _geoms.
+  CDReader copy_cdata(copy._cycler);
+  CDWriter cdata(_cycler);
+  cdata->_geoms = copy_cdata->_geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpGeomNode::
+~qpGeomNode() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::write_verbose
+//       Access: Published
+//  Description: Writes a detailed description of all the Geoms in the
+//               node.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+write_verbose(ostream &out, int indent_level) const {
+  CDReader cdata(_cycler);
+  PandaNode::write(out, indent_level);
+  Geoms::const_iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    indent(out, indent_level + 2) 
+      << *entry._geom << " (" << *entry._state << ")\n";
+    entry._geom->write_verbose(out, indent_level + 4);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+  out << " (" << get_num_geoms() << " geoms)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+write(ostream &out, int indent_level) const {
+  CDReader cdata(_cycler);
+  PandaNode::write(out, indent_level);
+  Geoms::const_iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    indent(out, indent_level + 2) 
+      << *entry._geom << " (" << *entry._state << ")\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::is_geom_node
+//       Access: Public, Virtual
+//  Description: A simple downcast check.  Returns true if this kind
+//               of node happens to inherit from GeomNode, false
+//               otherwise.
+//
+//               This is provided as a a faster alternative to calling
+//               is_of_type(GeomNode::get_class_type()), since this
+//               test is so important to rendering.
+////////////////////////////////////////////////////////////////////
+bool qpGeomNode::
+is_geom_node() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpGeomNode.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpGeomNode is encountered
+//               in the Bam file.  It should create the qpGeomNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpGeomNode::
+make_from_bam(const FactoryParams &params) {
+  qpGeomNode *node = new qpGeomNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::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 qpGeomNode.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+}

+ 118 - 0
panda/src/pgraph/qpgeomNode.h

@@ -0,0 +1,118 @@
+// Filename: qpgeomNode.h
+// Created by:  drose (22Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpGEOMNODE_H
+#define qpGEOMNODE_H
+
+#include "pandabase.h"
+
+#include "pandaNode.h"
+#include "pointerToArray.h"
+#include "geom.h"
+#include "pipelineCycler.h"
+#include "cycleData.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomNode
+// Description : A node that holds Geom objects, renderable pieces of
+//               geometry.  This is the primary kind of leaf node in
+//               the scene graph; almost all visible objects will be
+//               contained in a GeomNode somewhere.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpGeomNode : public PandaNode {
+PUBLISHED:
+  qpGeomNode(const string &name);
+
+public:
+  qpGeomNode(const qpGeomNode &copy);
+  void operator = (const qpGeomNode &copy);
+  virtual ~qpGeomNode();
+
+PUBLISHED:
+  INLINE int get_num_geoms() const;
+  INLINE Geom *get_geom(int n) const;
+  INLINE const RenderState *get_geom_state(int n) const;
+  INLINE void set_state(int n, const RenderState *state);
+
+  INLINE int add_geom(Geom *geom, const RenderState *state);
+  INLINE void remove_geom(int n);
+  INLINE void remove_all_geoms();
+
+  void write_verbose(ostream &out, int indent_level) const;
+
+public:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+  virtual bool is_geom_node() const;
+
+private:
+  class GeomEntry {
+  public:
+    INLINE GeomEntry(Geom *geom, const RenderState *state);
+    PT(Geom) _geom;
+    CPT(RenderState) _state;
+  };
+  typedef pvector<GeomEntry> Geoms;
+
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+
+    Geoms _geoms;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpGeomNode",
+                  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;
+
+  friend class PandaNode::Children;
+};
+
+#include "qpgeomNode.I"
+
+#endif

+ 12 - 1
panda/src/pgraph/renderAttrib.cxx

@@ -160,7 +160,7 @@ 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,
-//               TextureTransform) might combine in meaningful ways.
+//               TransformAttrib) might combine in meaningful ways.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) RenderAttrib::
 compose_impl(const RenderAttrib *other) const {
@@ -238,3 +238,14 @@ new_from_bam(RenderAttrib *attrib, BamReader *manager) {
   // reader expects that.
   return (RenderAttrib *)pointer.p();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::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 RenderAttrib.
+////////////////////////////////////////////////////////////////////
+void RenderAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 1 - 0
panda/src/pgraph/renderAttrib.h

@@ -80,6 +80,7 @@ public:
 
 protected:
   static TypedWritable *new_from_bam(RenderAttrib *attrib, BamReader *manager);
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:
   static TypeHandle get_class_type() {

+ 8 - 0
panda/src/pgraph/renderState.cxx

@@ -272,6 +272,14 @@ compose(const RenderState *other) const {
 
   cerr << "composing " << *this << " with " << *other << "\n";
 
+  // We handle empty state (identity) as a trivial special case.
+  if (is_empty()) {
+    return other;
+  }
+  if (other->is_empty()) {
+    return this;
+  }
+
   if (other == this) {
     // compose(this) has to be handled as a special case, because the
     // caching problem is so different.

+ 26 - 21
panda/src/pgraph/test_pgraph.cxx

@@ -19,7 +19,13 @@
 #include "pandaNode.h"
 #include "textureAttrib.h"
 #include "colorAttrib.h"
+#include "transformAttrib.h"
 #include "texture.h"
+#include "qpgeomNode.h"
+#include "geomTristrip.h"
+#include "geomTrifan.h"
+#include "qpcullTraverser.h"
+#include "cullHandler.h"
 
 void
 list_hierarchy(PandaNode *node, int indent_level) {
@@ -51,32 +57,31 @@ main(int argc, char *argv[]) {
   PandaNode *a1 = new PandaNode("a1");
   a->add_child(a1);
 
-  cerr << "\n";
-  list_hierarchy(root, 0);
-
-  cerr << "\nroot's attribs:\n";
-  root->get_state()->write(cerr, 0);
+  qpGeomNode *g1 = new qpGeomNode("g1");
+  a1->add_child(g1);
 
-  cerr << "\na's attribs:\n";
-  a->get_state()->write(cerr, 0);
+  Geom *geom1 = new GeomTristrip;
+  g1->add_geom(geom1, RenderState::make_empty());
+  Geom *geom2 = new GeomTrifan;
+  g1->add_geom(geom2, b->get_state());
 
-  cerr << "\nroot compose a:\n";
-  CPT(RenderState) result1 = root->get_state()->compose(a->get_state());
-  result1->write(cerr, 0);
+  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)));
 
-  //  a->clear_state();
-
-  cerr << "\nroot compose root:\n";
-  CPT(RenderState) result2 = root->get_state()->compose(root->get_state());
-  result2->write(cerr, 0);
+  cerr << "\n";
+  list_hierarchy(root, 0);
 
-  cerr << "\nroot compose a:\n";
-  CPT(RenderState) result3 = root->get_state()->compose(a->get_state());
-  result3->write(cerr, 0);
+  qpCullTraverser trav;
+  CullHandler cull_handler;
+  trav.set_cull_handler(&cull_handler);
+  cerr << "\n";
+  trav.traverse(root);
 
-  cerr << "\na compose root:\n";
-  CPT(RenderState) result4 = a->get_state()->compose(root->get_state());
-  result4->write(cerr, 0);
+  cerr << "\n";
+  trav.traverse(root);
 
+  cerr << "\n";
   return 0;
 }

+ 58 - 0
panda/src/pgraph/textureAttrib.cxx

@@ -17,6 +17,10 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "textureAttrib.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
 
 TypeHandle TextureAttrib::_type_handle;
 
@@ -97,3 +101,57 @@ RenderAttrib *TextureAttrib::
 make_default_impl() const {
   return new TextureAttrib;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               TextureAttrib.
+////////////////////////////////////////////////////////////////////
+void TextureAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TextureAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type TextureAttrib is encountered
+//               in the Bam file.  It should create the TextureAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *TextureAttrib::
+make_from_bam(const FactoryParams &params) {
+  TextureAttrib *attrib = new TextureAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return new_from_bam(attrib, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::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 TextureAttrib.
+////////////////////////////////////////////////////////////////////
+void TextureAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+}

+ 10 - 2
panda/src/pgraph/textureAttrib.h

@@ -50,15 +50,23 @@ protected:
 
 private:
   PT(Texture) _texture;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    RenderAttrib::init_type();
     register_type(_type_handle, "TextureAttrib",
-                  TypedWritableReferenceCount::get_class_type());
+                  RenderAttrib::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 172 - 0
panda/src/pgraph/transformAttrib.I

@@ -0,0 +1,172 @@
+// Filename: transformAttrib.I
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::Constructor
+//       Access: Private
+//  Description: Use TransformAttrib::make() to construct a new
+//               TransformAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE TransformAttrib::
+TransformAttrib() {
+  _flags = F_is_identity | F_singular_known;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::is_identity
+//       Access: Published
+//  Description: Returns true if the transform represents the identity
+//               matrix, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformAttrib::
+is_identity() const {
+  return ((_flags & F_is_identity) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::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::
+is_singular() const {
+  check_singular();
+  return ((_flags & F_is_singular) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::has_components
+//       Access: Published
+//  Description: Returns true if the transform can be described by
+//               separate pos, hpr, and scale components.  Most
+//               transforms we use in everyday life can be so
+//               described, but some kinds of transforms (for
+//               instance, those involving a skew) cannot.
+//
+//               This is not related to whether the transform was
+//               originally described componentwise.  Even a transform
+//               that was constructed with a 4x4 may return true here
+//               if the matrix is a simple affine matrix with no skew.
+//
+//               If this returns true, you may safely call get_pos(),
+//               etc., to retrieve the components.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformAttrib::
+has_components() const {
+  check_components();
+  return ((_flags & F_has_components) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::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::
+get_pos() const {
+  check_components();
+  nassertr(has_components(), _pos);
+  return _pos;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::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::
+get_hpr() const {
+  check_components();
+  nassertr(has_components(), _hpr);
+  return _hpr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::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::
+get_scale() const {
+  check_components();
+  nassertr(has_components(), _scale);
+  return _scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::get_mat
+//       Access: Published
+//  Description: Returns the matrix that describes the transform.
+////////////////////////////////////////////////////////////////////
+INLINE const LMatrix4f &TransformAttrib::
+get_mat() const {
+  check_mat();
+  return _mat;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::check_singular
+//       Access: Private
+//  Description: Ensures that we know whether the matrix is singular.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformAttrib::
+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();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::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::
+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();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformAttrib::check_mat
+//       Access: Private
+//  Description: Ensures that we know the overall matrix.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformAttrib::
+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();
+  }
+}

+ 308 - 0
panda/src/pgraph/transformAttrib.cxx

@@ -0,0 +1,308 @@
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 &params) {
+  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);
+}

+ 117 - 0
panda/src/pgraph/transformAttrib.h

@@ -0,0 +1,117 @@
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 &params);
+  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
+