Răsfoiți Sursa

pgraph billboards

David Rose 24 ani în urmă
părinte
comite
4fac7d8fbf

+ 8 - 6
panda/src/display/graphicsEngine.cxx

@@ -137,8 +137,8 @@ cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
     return;
   }
 
-  PandaNode *scene = camera_node->get_scene();
-  if (scene == (PandaNode *)NULL) {
+  NodeChain scene = camera_node->get_scene();
+  if (scene.is_empty()) {
     // No scene, no draw.
     return;
   }
@@ -161,20 +161,22 @@ cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
   // The world transform is computed from the camera's position; we
   // then might need to adjust it into the GSG's internal coordinate
   // system.
-  CPT(TransformState) world_transform = camera.get_rel_transform(NodeChain());
+  trav.set_camera_transform(scene.get_rel_transform(camera));
+
+  CPT(TransformState) render_transform = camera.get_rel_transform(scene);
   CoordinateSystem external_cs = gsg->get_coordinate_system();
   CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
   if (internal_cs != CS_default && internal_cs != external_cs) {
     CPT(TransformState) cs_transform = 
       TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
-    world_transform = cs_transform->compose(world_transform);
+    render_transform = cs_transform->compose(render_transform);
   }
-  trav.set_world_transform(world_transform);
+  trav.set_render_transform(render_transform);
   
   DisplayRegionStack old_dr = gsg->push_display_region(dr);
   gsg->prepare_display_region();
   
-  trav.traverse(scene);
+  trav.traverse(scene.node());
   
   gsg->pop_display_region(old_dr);
 }

+ 6 - 7
panda/src/egg2pg/qpeggLoader.cxx

@@ -24,6 +24,7 @@
 #include "transformState.h"
 #include "textureAttrib.h"
 #include "texturePool.h"
+#include "billboardAttrib.h"
 #include "qpgeomNode.h"
 #include "string_utils.h"
 #include "eggPrimitive.h"
@@ -985,13 +986,13 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent,
     bin = render_mode->get_bin();
   }
 
-  bucket._state = bucket._state->add(TextureAttrib::make_off());
+  bucket._state = bucket._state->add_attrib(TextureAttrib::make_off());
   if (egg_prim->has_texture()) {
     PT(EggTexture) egg_tex = egg_prim->get_texture();
 
     const TextureDef &def = _textures[egg_tex];
     if (def._texture != (const RenderAttrib *)NULL) {
-      bucket._state = bucket._state->add(def._texture);
+      bucket._state = bucket._state->add_attrib(def._texture);
       //      bucket._trans.set_transition(def._apply);
 
       // If neither the primitive nor the texture specified an alpha
@@ -1499,25 +1500,23 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
     node->set_transform(TransformState::make_mat(matf));
   }
 
-  /*
   // If the group has a billboard flag, apply that.
   switch (egg_group->get_billboard_type()) {
   case EggGroup::BT_point_camera_relative:
-    arc->set_transition(new BillboardTransition(BillboardTransition::point_eye()));
+    node->set_attrib(BillboardAttrib::make_point_eye());
     break;
 
   case EggGroup::BT_point_world_relative:
-    arc->set_transition(new BillboardTransition(BillboardTransition::point_world()));
+    node->set_attrib(BillboardAttrib::make_point_world());
     break;
 
   case EggGroup::BT_axis:
-    arc->set_transition(new BillboardTransition(BillboardTransition::axis()));
+    node->set_attrib(BillboardAttrib::make_axis());
     break;
 
   case EggGroup::BT_none:
     break;
   }
-  */
 
   /*
   if (egg_group->get_decal_flag()) {

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -6,6 +6,7 @@
   #define TARGET pgraph
   
   #define SOURCES \
+    billboardAttrib.h billboardAttrib.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
     config_pgraph.h \
@@ -29,6 +30,7 @@
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
   #define INCLUDED_SOURCES \
+    billboardAttrib.cxx \
     qpcamera.cxx \
     colorAttrib.cxx \
     config_pgraph.cxx \
@@ -57,6 +59,7 @@
   #endif
 
   #define INSTALL_HEADERS \
+    billboardAttrib.h billboardAttrib.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
     config_pgraph.h \

+ 153 - 0
panda/src/pgraph/billboardAttrib.I

@@ -0,0 +1,153 @@
+// Filename: billboardAttrib.I
+// Created by:  drose (27Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: BillboardAttrib::Constructor
+//       Access: Private
+//  Description: Use BillboardAttrib::make() to construct a new
+//               BillboardAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE BillboardAttrib::
+BillboardAttrib() {
+  _off = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make_axis
+//       Access: Published, Static
+//  Description: A convenience function to make a typical
+//               axis-rotating billboard.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) BillboardAttrib::
+make_axis() {
+  return make(LVector3f::up(), false, true, 
+              0.0f, NodeChain(), LPoint3f(0.0f, 0.0f, 0.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make_point_eye
+//       Access: Published, Static
+//  Description: A convenience function to make a typical
+//               eye-relative point-rotating billboard.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) BillboardAttrib::
+make_point_eye() {
+  return make(LVector3f::up(), true, false,
+              0.0f, NodeChain(), LPoint3f(0.0f, 0.0f, 0.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make_point_world
+//       Access: Published, Static
+//  Description: A convenience function to make a typical
+//               world-relative point-rotating billboard.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) BillboardAttrib::
+make_point_world() {
+  return make(LVector3f::up(), false, false,
+              0.0f, NodeChain(), LPoint3f(0.0f, 0.0f, 0.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::is_off
+//       Access: Published
+//  Description: Returns true if the BillboardAttrib is an 'off'
+//               BillboardAttrib, indicating that it does not enable
+//               billboarding.  This kind of BillboardAttrib isn't
+//               particularly useful and isn't normally created or
+//               stored in the graph; it might be implicitly
+//               discovered as the result of a
+//               NodeChain::get_rel_state().
+////////////////////////////////////////////////////////////////////
+INLINE bool BillboardAttrib::
+is_off() const {
+  return _off;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_up_vector
+//       Access: Published
+//  Description: Returns the up vector in effect for this billboard.
+////////////////////////////////////////////////////////////////////
+INLINE const LVector3f &BillboardAttrib::
+get_up_vector() const {
+  return _up_vector;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_eye_relative
+//       Access: Published
+//  Description: Returns true if this billboard interprets the up
+//               vector relative to the camera, or false if it is
+//               relative to the world.
+////////////////////////////////////////////////////////////////////
+INLINE bool BillboardAttrib::
+get_eye_relative() const {
+  return _eye_relative;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_axial_rotate
+//       Access: Published
+//  Description: Returns true if this billboard rotates only around
+//               the axis of the up vector, or false if it rotates
+//               freely in three dimensions.
+////////////////////////////////////////////////////////////////////
+INLINE bool BillboardAttrib::
+get_axial_rotate() const {
+  return _axial_rotate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_offset
+//       Access: Published
+//  Description: Returns the distance toward the camera (or the
+//               look_at_point) the billboard is moved towards, after
+//               rotating.  This can be used to ensure the billboard
+//               is not obscured by nearby geometry.
+////////////////////////////////////////////////////////////////////
+INLINE float BillboardAttrib::
+get_offset() const {
+  return _offset;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_look_at
+//       Access: Published
+//  Description: Returns the node this billboard will rotate to look
+//               towards.  If this is empty, it means the billboard
+//               will rotate towards the current camera node, wherever
+//               that might be.
+////////////////////////////////////////////////////////////////////
+INLINE const NodeChain &BillboardAttrib::
+get_look_at() const {
+  return _look_at;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::get_look_at_point
+//       Access: Published
+//  Description: Returns the point, relative to the look_at node,
+//               towards which the billboard will rotate.  Normally
+//               this is (0, 0, 0).
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint3f &BillboardAttrib::
+get_look_at_point() const {
+  return _look_at_point;
+}

+ 255 - 0
panda/src/pgraph/billboardAttrib.cxx

@@ -0,0 +1,255 @@
+// Filename: billboardAttrib.cxx
+// Created by:  drose (27Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "billboardAttrib.h"
+#include "look_at.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle BillboardAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new BillboardAttrib object with the
+//               indicated properties.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) BillboardAttrib::
+make(const LVector3f &up_vector, bool eye_relative,
+     bool axial_rotate, float offset, const NodeChain &look_at,
+     const LPoint3f &look_at_point) {
+  BillboardAttrib *attrib = new BillboardAttrib;
+  attrib->_up_vector = up_vector;
+  attrib->_eye_relative = eye_relative;
+  attrib->_axial_rotate = axial_rotate;
+  attrib->_offset = offset;
+  attrib->_look_at = look_at;
+  attrib->_look_at_point = look_at_point;
+  attrib->_off = false;
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BillboardAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  if (is_off()) {
+    out << "(off)";
+  } else {
+    if (_axial_rotate) {
+      out << "(axis";
+    } else {
+      out << "(point";
+    }
+    if (!_up_vector.almost_equal(LVector3f::up())) {
+      out << " up " << _up_vector;
+    }
+    if (_eye_relative) {
+      out << " eye";
+    }
+    if (_offset != 0.0f) {
+      out << " offset " << _offset;
+    }
+    if (!_look_at.is_empty()) {
+      out << " look at " << _look_at;
+    }
+    if (!_look_at_point.almost_equal(LPoint3f(0.0f, 0.0f, 0.0f))) {
+      out << " look at point " << _look_at_point;
+    }
+    out << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::do_billboard
+//       Access: Public
+//  Description: Computes the appropriate transform to apply to the
+//               billboarded geometry, given its current net
+//               transform, and the camera's inverse net transform.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) BillboardAttrib::
+do_billboard(const TransformState *net_transform,
+             const TransformState *camera_transform) const {
+  // Determine the relative transform to our camera (or other look_at
+  // coordinate space).
+  CPT(TransformState) rel_transform =
+    net_transform->invert_compose(camera_transform);
+  const LMatrix4f &rel_mat = rel_transform->get_mat();
+
+  // Determine the look_at point in the camera space.
+  LVector3f camera_pos, up;
+
+  // If this is an eye-relative Billboard, then (a) the up vector is
+  // relative to the camera, not to the world, and (b) the look
+  // direction is towards the plane that contains the camera,
+  // perpendicular to the forward direction, not directly to the
+  // camera.
+
+  if (_eye_relative) {
+    up = _up_vector * rel_mat;
+    camera_pos = LVector3f::forward() * rel_mat;
+
+  } else {
+//  camera_pos= -rel_mat.get_row3(3);
+
+    camera_pos[0] = -rel_mat(3,0);
+    camera_pos[1] = -rel_mat(3,1);
+    camera_pos[2] = -rel_mat(3,2);
+
+    up = _up_vector;
+  }
+
+  // Now determine the rotation matrix for the Billboard.
+  LMatrix4f rotate;
+  if (_axial_rotate) {
+    heads_up(rotate, camera_pos, up);
+  } else {
+    look_at(rotate, camera_pos, up);
+  }
+
+  // Also slide the billboard geometry towards the camera according to
+  // the offset factor.
+  if (_offset != 0.0f) {
+    LVector3f translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2));
+    translate.normalize();
+    translate *= _offset;
+    rotate.set_row(3, translate);
+  }
+
+  return TransformState::make_mat(rotate);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived BillboardAttrib
+//               types to return a unique number indicating whether
+//               this BillboardAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two BillboardAttrib 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 BillboardAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int BillboardAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const BillboardAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+
+  if (_axial_rotate != ta->_axial_rotate) {
+    return _axial_rotate - ta->_axial_rotate;
+  }
+  if (_eye_relative != ta->_eye_relative) {
+    return _eye_relative - ta->_eye_relative;
+  }
+  if (_offset != ta->_offset) {
+    return _offset < ta->_offset ? -1 : 1;
+  }
+  int compare = _up_vector.compare_to(ta->_up_vector);
+  if (compare != 0) {
+    return compare;
+  }
+  compare = _look_at.compare_to(ta->_look_at);
+  if (compare != 0) {
+    return compare;
+  }
+  compare = _look_at_point.compare_to(ta->_look_at_point);
+  if (compare != 0) {
+    return compare;
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived BillboardAttrib
+//               types to specify what the default property for a
+//               BillboardAttrib of this type should be.
+//
+//               This should return a newly-allocated BillboardAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of BillboardAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *BillboardAttrib::
+make_default_impl() const {
+  return new BillboardAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               BillboardAttrib.
+////////////////////////////////////////////////////////////////////
+void BillboardAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void BillboardAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type BillboardAttrib is encountered
+//               in the Bam file.  It should create the BillboardAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *BillboardAttrib::
+make_from_bam(const FactoryParams &params) {
+  BillboardAttrib *attrib = new BillboardAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return new_from_bam(attrib, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardAttrib::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 BillboardAttrib.
+////////////////////////////////////////////////////////////////////
+void BillboardAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+}

+ 105 - 0
panda/src/pgraph/billboardAttrib.h

@@ -0,0 +1,105 @@
+// Filename: billboardAttrib.h
+// Created by:  drose (27Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 BILLBOARDATTRIB_H
+#define BILLBOARDATTRIB_H
+
+#include "pandabase.h"
+
+#include "renderAttrib.h"
+#include "luse.h"
+#include "nodeChain.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : BillboardAttrib
+// Description : Indicates that geometry at this node should
+//               automatically rotate to face the camera, or any other
+//               arbitrary node.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA BillboardAttrib : public RenderAttrib {
+private:
+  INLINE BillboardAttrib();
+
+PUBLISHED:
+  static CPT(RenderAttrib) make(const LVector3f &up_vector,
+                                bool eye_relative,
+                                bool axial_rotate,
+                                float offset,
+                                const NodeChain &look_at,
+                                const LPoint3f &look_at_point);
+  INLINE static CPT(RenderAttrib) make_axis();
+  INLINE static CPT(RenderAttrib) make_point_eye();
+  INLINE static CPT(RenderAttrib) make_point_world();
+
+  INLINE bool is_off() const;
+  INLINE const LVector3f &get_up_vector() const;
+  INLINE bool get_eye_relative() const;
+  INLINE bool get_axial_rotate() const;
+  INLINE float get_offset() const;
+  INLINE const NodeChain &get_look_at() const;
+  INLINE const LPoint3f &get_look_at_point() const;
+
+public:
+  virtual void output(ostream &out) const;
+
+  CPT(TransformState) do_billboard(const TransformState *net_transform,
+                                   const TransformState *camera_transform) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+private:
+  bool _off;
+  LVector3f _up_vector;
+  bool _eye_relative;
+  bool _axial_rotate;
+  float _offset;
+  NodeChain _look_at;
+  LPoint3f _look_at_point;
+
+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, "BillboardAttrib",
+                  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 "billboardAttrib.I"
+
+#endif
+

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

@@ -18,6 +18,7 @@
 
 #include "config_pgraph.h"
 
+#include "billboardAttrib.h"
 #include "qpcamera.h"
 #include "colorAttrib.h"
 #include "qpgeomNode.h"
@@ -57,6 +58,7 @@ init_libpgraph() {
   }
   initialized = true;
 
+  BillboardAttrib::init_type();
   qpCamera::init_type();
   ColorAttrib::init_type();
   qpGeomNode::init_type();
@@ -70,6 +72,7 @@ init_libpgraph() {
   ColorAttrib::init_type();
   TransformState::init_type();
 
+  BillboardAttrib::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
   qpGeomNode::register_with_read_factory();
   PandaNode::register_with_read_factory();

+ 2 - 2
panda/src/pgraph/pandaNode.I

@@ -249,7 +249,7 @@ get_child_sort(int n) const {
 INLINE void PandaNode::
 set_attrib(const RenderAttrib *attrib, int override) {
   CDWriter cdata(_cycler);
-  cdata->_state = cdata->_state->add(attrib, override);
+  cdata->_state = cdata->_state->add_attrib(attrib, override);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -282,7 +282,7 @@ get_attrib(TypeHandle type) const {
 INLINE void PandaNode::
 clear_attrib(TypeHandle type) {
   CDWriter cdata(_cycler);
-  cdata->_state = cdata->_state->remove(type);
+  cdata->_state = cdata->_state->remove_attrib(type);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/pgraph/qpcamera.I

@@ -48,7 +48,7 @@ is_active() const {
 //               represent the root of any subgraph.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpCamera::
-set_scene(PandaNode *scene) {
+set_scene(const NodeChain &scene) {
   _scene = scene;
 }
 
@@ -58,7 +58,7 @@ set_scene(PandaNode *scene) {
 //  Description: Returns the scene that will be rendered by the
 //               camera.  See set_scene().
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode *qpCamera::
+INLINE const NodeChain &qpCamera::
 get_scene() const {
   return _scene;
 }

+ 1 - 2
panda/src/pgraph/qpcamera.cxx

@@ -31,8 +31,7 @@ TypeHandle qpCamera::_type_handle;
 qpCamera::
 qpCamera(const string &name) :
   qpLensNode(name),
-  _active(true),
-  _scene((PandaNode *)NULL)
+  _active(true)
 {
 }
 

+ 4 - 4
panda/src/pgraph/qpcamera.h

@@ -22,9 +22,9 @@
 #include "pandabase.h"
 
 #include "qplensNode.h"
+#include "nodeChain.h"
 
 class DisplayRegion;
-class PandaNode;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : qpCamera
@@ -49,8 +49,8 @@ PUBLISHED:
   INLINE void set_active(bool active);
   INLINE bool is_active() const;
 
-  INLINE void set_scene(PandaNode *scene);
-  INLINE PandaNode *get_scene() const;
+  INLINE void set_scene(const NodeChain &scene);
+  INLINE const NodeChain &get_scene() const;
 
   INLINE int get_num_display_regions() const;
   INLINE DisplayRegion *get_display_region(int n) const;
@@ -60,7 +60,7 @@ private:
   void remove_display_region(DisplayRegion *display_region);
 
   bool _active;
-  PandaNode *_scene;
+  NodeChain _scene;
 
   typedef pvector<DisplayRegion *> DisplayRegions;
   DisplayRegions _display_regions;

+ 42 - 11
panda/src/pgraph/qpcullTraverser.cxx

@@ -19,6 +19,7 @@
 #include "qpcullTraverser.h"
 #include "transformState.h"
 #include "renderState.h"
+#include "billboardAttrib.h"
 #include "cullHandler.h"
 #include "dcast.h"
 #include "qpgeomNode.h"
@@ -31,7 +32,8 @@
 qpCullTraverser::
 qpCullTraverser() {
   _initial_state = RenderState::make_empty();
-  _world_transform = DCAST(TransformState, TransformState::make_identity());
+  _camera_transform = DCAST(TransformState, TransformState::make_identity());
+  _render_transform = DCAST(TransformState, TransformState::make_identity());
   _cull_handler = (CullHandler *)NULL;
 }
 
@@ -48,14 +50,28 @@ set_initial_state(const RenderState *initial_state) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpCullTraverser::set_world_transform
+//     Function: qpCullTraverser::set_camera_transform
 //       Access: Public
-//  Description: Specifies the position of the world relative to the
-//               camera.
+//  Description: Specifies the position of the camera relative to the
+//               starting node, without any compensating
+//               coordinate-system transforms that might have been
+//               introduced for the purposes of rendering.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-set_world_transform(const TransformState *world_transform) {
-  _world_transform = world_transform;
+set_camera_transform(const TransformState *camera_transform) {
+  _camera_transform = camera_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_render_transform
+//       Access: Public
+//  Description: Specifies the position of the starting node relative
+//               to the camera, pretransformed as appropriate for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_render_transform(const TransformState *render_transform) {
+  _render_transform = render_transform;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -78,7 +94,8 @@ void qpCullTraverser::
 traverse(PandaNode *root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
 
-  r_traverse(root, _world_transform, _initial_state, 0);
+  r_traverse(root, _render_transform, TransformState::make_identity(),
+             _initial_state, 0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -87,11 +104,25 @@ traverse(PandaNode *root) {
 //  Description: The recursive traversal implementation.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-r_traverse(PandaNode *node, const TransformState *transform,
+r_traverse(PandaNode *node, 
+           const TransformState *render_transform,
+           const TransformState *net_transform,
            const RenderState *state, int flags) {
-  CPT(TransformState) next_transform = transform->compose(node->get_transform());
+  CPT(TransformState) next_render_transform = 
+    render_transform->compose(node->get_transform());
+  CPT(TransformState) next_net_transform = 
+    net_transform->compose(node->get_transform());
   CPT(RenderState) next_state = state->compose(node->get_state());
 
+  const BillboardAttrib *billboard = state->get_billboard();
+  if (billboard != (const BillboardAttrib *)NULL) {
+    // Got to apply a billboard transform here.
+    CPT(TransformState) billboard_transform = 
+      billboard->do_billboard(net_transform, _camera_transform);
+    next_render_transform = next_render_transform->compose(billboard_transform);
+    next_net_transform = next_net_transform->compose(billboard_transform);
+  }
+
   if (node->is_geom_node()) {
     qpGeomNode *geom_node;
     DCAST_INTO_V(geom_node, node);
@@ -101,7 +132,7 @@ r_traverse(PandaNode *node, const TransformState *transform,
       Geom *geom = geom_node->get_geom(i);
       CPT(RenderState) geom_state = 
         next_state->compose(geom_node->get_geom_state(i));
-      _cull_handler->record_geom(geom, next_transform, geom_state);
+      _cull_handler->record_geom(geom, next_render_transform, geom_state);
     }
   }
 
@@ -109,6 +140,6 @@ r_traverse(PandaNode *node, const TransformState *transform,
   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_transform, next_state, flags);
+    r_traverse(cr.get_child(i), next_render_transform, next_net_transform, next_state, flags);
   }
 }

+ 6 - 3
panda/src/pgraph/qpcullTraverser.h

@@ -41,17 +41,20 @@ public:
   qpCullTraverser();
 
   void set_initial_state(const RenderState *initial_state);
-  void set_world_transform(const TransformState *world_transform);
+  void set_camera_transform(const TransformState *camera_transform);
+  void set_render_transform(const TransformState *render_transform);
   void set_cull_handler(CullHandler *cull_handler);
 
   void traverse(PandaNode *root);
 
 private:
-  void r_traverse(PandaNode *node, const TransformState *transform,
+  void r_traverse(PandaNode *node, const TransformState *render_transform,
+                  const TransformState *net_transform,
                   const RenderState *state, int flags);
 
   CPT(RenderState) _initial_state;
-  CPT(TransformState) _world_transform;
+  CPT(TransformState) _camera_transform;
+  CPT(TransformState) _render_transform;
   CullHandler *_cull_handler;
 };
 

+ 19 - 0
panda/src/pgraph/renderState.I

@@ -148,3 +148,22 @@ get_override(int n) const {
   nassertr(n >= 0 && n < (int)_attributes.size(), 0);
   return _attributes[n]._override;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_billboard
+//       Access: Public
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a BillboardAttrib on this state.  It returns a
+//               pointer to the BillboardAttrib, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const BillboardAttrib *RenderState::
+get_billboard() const {
+  if ((_flags & F_checked_billboard) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal billboard cache.
+    ((RenderState *)this)->determine_billboard();
+  }
+  return _billboard;
+}

+ 38 - 4
panda/src/pgraph/renderState.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "renderState.h"
+#include "billboardAttrib.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "datagramIterator.h"
@@ -38,6 +39,7 @@ RenderState::
 RenderState() {
   _saved_entry = _states.end();
   _self_compose = (RenderState *)NULL;
+  _flags = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -399,7 +401,7 @@ invert_compose(const RenderState *other) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::add
+//     Function: RenderState::add_attrib
 //       Access: Published
 //  Description: Returns a new RenderState object that represents the
 //               same as the source state, with the new RenderAttrib
@@ -407,7 +409,7 @@ invert_compose(const RenderState *other) const {
 //               same type, it is replaced.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderState) RenderState::
-add(const RenderAttrib *attrib, int override) const {
+add_attrib(const RenderAttrib *attrib, int override) const {
   RenderState *new_state = new RenderState;
   back_insert_iterator<Attributes> result = 
     back_inserter(new_state->_attributes);
@@ -441,14 +443,14 @@ add(const RenderAttrib *attrib, int override) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::remove
+//     Function: RenderState::remove_attrib
 //       Access: Published
 //  Description: Returns a new RenderState object that represents the
 //               same as the source state, with the indicated
 //               RenderAttrib removed
 ////////////////////////////////////////////////////////////////////
 CPT(RenderState) RenderState::
-remove(TypeHandle type) const {
+remove_attrib(TypeHandle type) const {
   RenderState *new_state = new RenderState;
   back_insert_iterator<Attributes> result = 
     back_inserter(new_state->_attributes);
@@ -466,6 +468,23 @@ remove(TypeHandle type) const {
   return return_new(new_state);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_attrib
+//       Access: Published, Virtual
+//  Description: Looks for a RenderAttrib of the indicated type in the
+//               state, and returns it if it is found, or NULL if it
+//               is not.
+////////////////////////////////////////////////////////////////////
+const RenderAttrib *RenderState::
+get_attrib(TypeHandle type) const {
+  Attributes::const_iterator ai;
+  ai = _attributes.find(Attribute(type));
+  if (ai != _attributes.end()) {
+    return (*ai)._attrib;
+  }
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::output
 //       Access: Published, Virtual
@@ -807,6 +826,21 @@ do_invert_compose(const RenderState *other) const {
   return return_new(new_state);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_billboard
+//       Access: Private
+//  Description: This is the private implementation of
+//               get_billboard().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_billboard() {
+  const RenderAttrib *attrib = get_attrib(BillboardAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    _billboard = DCAST(BillboardAttrib, attrib);
+  }
+  _flags |= F_checked_billboard;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::register_with_read_factory
 //       Access: Public, Static

+ 16 - 2
panda/src/pgraph/renderState.h

@@ -28,6 +28,7 @@
 #include "ordered_vector.h"
 
 class GraphicsStateGuardianBase;
+class BillboardAttrib;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : RenderState
@@ -77,13 +78,17 @@ PUBLISHED:
   CPT(RenderState) compose(const RenderState *other) const;
   CPT(RenderState) invert_compose(const RenderState *other) const;
 
-  CPT(RenderState) add(const RenderAttrib *attrib, int override = 0) const;
-  CPT(RenderState) remove(TypeHandle type) const;
+  CPT(RenderState) add_attrib(const RenderAttrib *attrib, int override = 0) const;
+  CPT(RenderState) remove_attrib(TypeHandle type) const;
+
+  const RenderAttrib *get_attrib(TypeHandle type) const;
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level) const;
 
 public:
+  INLINE const BillboardAttrib *get_billboard() const;
+
   CPT(RenderState) issue_delta_modify(const RenderState *other, 
                                       GraphicsStateGuardianBase *gsg) const;
   CPT(RenderState) issue_delta_set(const RenderState *other, 
@@ -93,6 +98,7 @@ private:
   static CPT(RenderState) return_new(RenderState *state);
   CPT(RenderState) do_compose(const RenderState *other) const;
   CPT(RenderState) do_invert_compose(const RenderState *other) const;
+  void determine_billboard();
 
 private:
   typedef pset<const RenderState *, IndirectLess<RenderState> > States;
@@ -145,6 +151,14 @@ private:
   typedef ov_set<Attribute> Attributes;
   Attributes _attributes;
 
+  // We cache the pointer to the BillboardAttrib stored in the state,
+  // if there is one.
+  const BillboardAttrib *_billboard;
+  enum Flags {
+    F_checked_billboard    = 0x0001,
+  };
+  short _flags;
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

+ 1 - 1
panda/src/testbed/pview.cxx

@@ -270,7 +270,7 @@ main(int argc, char *argv[]) {
   // Now we just need to make a scene graph for the camera to render.
   PT(PandaNode) render = new PandaNode("render");
   render->add_child(camera);
-  camera->set_scene(render);
+  camera->set_scene(NodeChain(render));
 
   // Set up a data graph for tracking user input.  For now, this uses
   // the old-style graph interface.