Browse Source

add CompassEffect, robustify billboards

David Rose 23 years ago
parent
commit
476fb91e31

+ 3 - 5
panda/src/display/graphicsEngine.cxx

@@ -275,14 +275,12 @@ setup_scene(const NodePath &camera, GraphicsStateGuardian *gsg) {
   // The render transform is the same as the world transform, except
   // The render transform is the same as the world transform, except
   // it is converted into the GSG's internal coordinate system.  This
   // it is converted into the GSG's internal coordinate system.  This
   // is the transform that the GSG will apply to all of its vertices.
   // is the transform that the GSG will apply to all of its vertices.
-  CPT(TransformState) render_transform = world_transform;
-
+  CPT(TransformState) cs_transform = TransformState::make_identity();
   CoordinateSystem external_cs = gsg->get_coordinate_system();
   CoordinateSystem external_cs = gsg->get_coordinate_system();
   CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
   CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
   if (internal_cs != CS_default && internal_cs != external_cs) {
   if (internal_cs != CS_default && internal_cs != external_cs) {
-    CPT(TransformState) cs_transform = 
+    cs_transform = 
       TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
       TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
-    render_transform = cs_transform->compose(render_transform);
   }
   }
 
 
   scene_setup->set_scene_root(scene_root);
   scene_setup->set_scene_root(scene_root);
@@ -291,7 +289,7 @@ setup_scene(const NodePath &camera, GraphicsStateGuardian *gsg) {
   scene_setup->set_lens(lens);
   scene_setup->set_lens(lens);
   scene_setup->set_camera_transform(camera_transform);
   scene_setup->set_camera_transform(camera_transform);
   scene_setup->set_world_transform(world_transform);
   scene_setup->set_world_transform(world_transform);
-  scene_setup->set_render_transform(render_transform);
+  scene_setup->set_cs_transform(cs_transform);
 
 
   return scene_setup;
   return scene_setup;
 }
 }

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

@@ -19,6 +19,7 @@
     colorBlendAttrib.I colorBlendAttrib.h \
     colorBlendAttrib.I colorBlendAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
     colorWriteAttrib.I colorWriteAttrib.h \
     colorWriteAttrib.I colorWriteAttrib.h \
+    compassEffect.I compassEffect.h \
     config_pgraph.h \
     config_pgraph.h \
     cullBin.I cullBin.h \
     cullBin.I cullBin.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinAttrib.I cullBinAttrib.h \
@@ -100,6 +101,7 @@
     colorBlendAttrib.cxx \
     colorBlendAttrib.cxx \
     colorScaleAttrib.cxx \
     colorScaleAttrib.cxx \
     colorWriteAttrib.cxx \
     colorWriteAttrib.cxx \
+    compassEffect.cxx \
     config_pgraph.cxx \
     config_pgraph.cxx \
     cullBin.cxx \
     cullBin.cxx \
     cullBinAttrib.cxx \
     cullBinAttrib.cxx \
@@ -180,6 +182,7 @@
     colorBlendAttrib.I colorBlendAttrib.h \
     colorBlendAttrib.I colorBlendAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
     colorWriteAttrib.I colorWriteAttrib.h \
     colorWriteAttrib.I colorWriteAttrib.h \
+    compassEffect.I compassEffect.h \
     config_pgraph.h \
     config_pgraph.h \
     cullBin.I cullBin.h \
     cullBin.I cullBin.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinAttrib.I cullBinAttrib.h \

+ 6 - 6
panda/src/pgraph/billboardEffect.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "billboardEffect.h"
 #include "billboardEffect.h"
+#include "nodePath.h"
 #include "look_at.h"
 #include "look_at.h"
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
@@ -105,6 +106,10 @@ do_billboard(const TransformState *net_transform,
              const TransformState *camera_transform) const {
              const TransformState *camera_transform) const {
   // Determine the relative transform to our camera (or other look_at
   // Determine the relative transform to our camera (or other look_at
   // coordinate space).
   // coordinate space).
+  if (!_look_at.is_empty()) {
+    camera_transform = _look_at.get_net_transform();
+  }
+
   CPT(TransformState) rel_transform =
   CPT(TransformState) rel_transform =
     net_transform->invert_compose(camera_transform);
     net_transform->invert_compose(camera_transform);
   const LMatrix4f &rel_mat = rel_transform->get_mat();
   const LMatrix4f &rel_mat = rel_transform->get_mat();
@@ -123,13 +128,8 @@ do_billboard(const TransformState *net_transform,
     camera_pos = LVector3f::forward() * rel_mat;
     camera_pos = LVector3f::forward() * rel_mat;
 
 
   } else {
   } 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;
     up = _up_vector;
+    camera_pos = -(_look_at_point * rel_mat);
   }
   }
 
 
   // Now determine the rotation matrix for the Billboard.
   // Now determine the rotation matrix for the Billboard.

+ 28 - 0
panda/src/pgraph/compassEffect.I

@@ -0,0 +1,28 @@
+// Filename: compassEffect.I
+// Created by:  drose (16Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: CompassEffect::Constructor
+//       Access: Private
+//  Description: Use CompassEffect::make() to construct a new
+//               CompassEffect object.
+////////////////////////////////////////////////////////////////////
+INLINE CompassEffect::
+CompassEffect() {
+}

+ 210 - 0
panda/src/pgraph/compassEffect.cxx

@@ -0,0 +1,210 @@
+// Filename: compassEffect.cxx
+// Created by:  drose (16Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "compassEffect.h"
+#include "config_pgraph.h"
+#include "nodePath.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle CompassEffect::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::make
+//       Access: Published, Static
+//  Description: Constructs a new CompassEffect object.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffect) CompassEffect::
+make(const NodePath &reference) {
+  CompassEffect *effect = new CompassEffect;
+  effect->_reference = reference;
+  return return_new(effect);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::safe_to_transform
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of RenderEffect by calling the
+//               xform() method, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CompassEffect::
+safe_to_transform() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CompassEffect::
+output(ostream &out) const {
+  if (!_reference.is_empty()) {
+    RenderEffect::output(out);
+  } else {
+    out << get_type() << ":";
+    if (!_reference.is_empty()) {
+      out << " reference " << _reference;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::do_compass
+//       Access: Public
+//  Description: Computes the appropriate transform to rotate the node
+//               according to the reference node, or to the root
+//               transform if there is no reference node.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) CompassEffect::
+do_compass(const TransformState *net_transform,
+           const TransformState *node_transform) const {
+  if (!net_transform->has_components() || !node_transform->has_quat()) {
+    // If we don't have a decomposable transform, we can't do anything here.
+    pgraph_cat.warning()
+      << "CompassEffect unable to adjust non-decomposable transform\n";
+    return TransformState::make_identity();
+  }
+
+  // Compute just the rotation part of the transform we want.
+  CPT(TransformState) want_rot = TransformState::make_identity();
+  if (!_reference.is_empty()) {
+    CPT(TransformState) rel_transform = _reference.get_net_transform();
+    if (!rel_transform->has_quat()) {
+      pgraph_cat.warning()
+        << "CompassEffect unable to reference non-decomposable transform\n";
+    } else {
+      want_rot = TransformState::make_quat(rel_transform->get_quat());
+    }
+  }
+
+  want_rot = 
+    want_rot->compose(TransformState::make_quat(node_transform->get_quat()));
+
+  // Now compute the net transform we want to achieve.  This is the
+  // same as the net transform we were given, except the rotation
+  // component is replaced by our desired rotation.
+  CPT(TransformState) want_transform = 
+    TransformState::make_pos_quat_scale(net_transform->get_pos(),
+                                        want_rot->get_quat(),
+                                        net_transform->get_scale());
+
+  // Now compute the transform that will convert net_transform to
+  // want_transform.  This is inv(net_transform) * want_transform.
+  return net_transform->invert_compose(want_transform);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived CompassEffect
+//               types to return a unique number indicating whether
+//               this CompassEffect is equivalent to the other one.
+//
+//               This should return 0 if the two CompassEffect 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 CompassEffect
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int CompassEffect::
+compare_to_impl(const RenderEffect *other) const {
+  const CompassEffect *ta;
+  DCAST_INTO_R(ta, other, 0);
+
+  int compare = _reference.compare_to(ta->_reference);
+  if (compare != 0) {
+    return compare;
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived CompassEffect
+//               types to specify what the default property for a
+//               CompassEffect of this type should be.
+//
+//               This should return a newly-allocated CompassEffect of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of CompassEffect is.
+////////////////////////////////////////////////////////////////////
+RenderEffect *CompassEffect::
+make_default_impl() const {
+  return new CompassEffect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               CompassEffect.
+////////////////////////////////////////////////////////////////////
+void CompassEffect::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CompassEffect::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderEffect::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type CompassEffect is encountered
+//               in the Bam file.  It should create the CompassEffect
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *CompassEffect::
+make_from_bam(const FactoryParams &params) {
+  CompassEffect *effect = new CompassEffect;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  effect->fillin(scan, manager);
+
+  return effect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CompassEffect::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 CompassEffect.
+////////////////////////////////////////////////////////////////////
+void CompassEffect::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderEffect::fillin(scan, manager);
+}

+ 85 - 0
panda/src/pgraph/compassEffect.h

@@ -0,0 +1,85 @@
+// Filename: compassEffect.h
+// Created by:  drose (16Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 COMPASSEFFECT_H
+#define COMPASSEFFECT_H
+
+#include "pandabase.h"
+
+#include "renderEffect.h"
+#include "luse.h"
+#include "nodePath.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CompassEffect
+// Description : Indicates that geometry at this node should
+//               automatically rotate to face the camera, or any other
+//               arbitrary node.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CompassEffect : public RenderEffect {
+private:
+  INLINE CompassEffect();
+
+PUBLISHED:
+  static CPT(RenderEffect) make(const NodePath &reference);
+
+public:
+  virtual bool safe_to_transform() const;
+  virtual void output(ostream &out) const;
+
+  CPT(TransformState) CompassEffect::
+  do_compass(const TransformState *net_transform,
+             const TransformState *node_transform) const;
+
+protected:
+  virtual int compare_to_impl(const RenderEffect *other) const;
+  virtual RenderEffect *make_default_impl() const;
+
+private:
+  NodePath _reference;
+
+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() {
+    RenderEffect::init_type();
+    register_type(_type_handle, "CompassEffect",
+                  RenderEffect::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 "compassEffect.I"
+
+#endif
+

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

@@ -27,6 +27,7 @@
 #include "colorBlendAttrib.h"
 #include "colorBlendAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorWriteAttrib.h"
 #include "colorWriteAttrib.h"
+#include "compassEffect.h"
 #include "cullFaceAttrib.h"
 #include "cullFaceAttrib.h"
 #include "cullBin.h"
 #include "cullBin.h"
 #include "cullBinAttrib.h"
 #include "cullBinAttrib.h"
@@ -167,6 +168,7 @@ init_libpgraph() {
   ColorBlendAttrib::init_type();
   ColorBlendAttrib::init_type();
   ColorScaleAttrib::init_type();
   ColorScaleAttrib::init_type();
   ColorWriteAttrib::init_type();
   ColorWriteAttrib::init_type();
+  CompassEffect::init_type();
   CullFaceAttrib::init_type();
   CullFaceAttrib::init_type();
   CullBin::init_type();
   CullBin::init_type();
   CullBinAttrib::init_type();
   CullBinAttrib::init_type();
@@ -230,6 +232,7 @@ init_libpgraph() {
   BillboardEffect::register_with_read_factory();
   BillboardEffect::register_with_read_factory();
   Camera::register_with_read_factory();
   Camera::register_with_read_factory();
   ClipPlaneAttrib::register_with_read_factory();
   ClipPlaneAttrib::register_with_read_factory();
+  CompassEffect::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
   ColorBlendAttrib::register_with_read_factory();
   ColorBlendAttrib::register_with_read_factory();
   ColorScaleAttrib::register_with_read_factory();
   ColorScaleAttrib::register_with_read_factory();

+ 16 - 0
panda/src/pgraph/cullTraverserData.cxx

@@ -24,6 +24,7 @@
 #include "textureAttrib.h"
 #include "textureAttrib.h"
 #include "renderModeAttrib.h"
 #include "renderModeAttrib.h"
 #include "billboardEffect.h"
 #include "billboardEffect.h"
+#include "compassEffect.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::apply_transform_and_state
 //     Function: CullTraverserData::apply_transform_and_state
@@ -73,6 +74,21 @@ apply_transform_and_state(CullTraverser *trav) {
   _state = _state->compose(node_state);
   _state = _state->compose(node_state);
 
 
   const RenderEffects *node_effects = node()->get_effects();
   const RenderEffects *node_effects = node()->get_effects();
+
+  const CompassEffect *compass = node_effects->get_compass();
+  if (compass != (const CompassEffect *)NULL) {
+    // Got to apply a compass transform here.
+    CPT(TransformState) compass_transform = 
+      compass->do_compass(_net_transform, node_transform);
+    _render_transform = _render_transform->compose(compass_transform);
+    _net_transform = _net_transform->compose(compass_transform);
+
+    // We can't reliably cull within a compass, because the geometry
+    // might get rotated out of its bounding volume.  So once we get
+    // within a compass, we consider it all visible.
+    _view_frustum = (GeometricBoundingVolume *)NULL;
+  }
+
   const BillboardEffect *billboard = node_effects->get_billboard();
   const BillboardEffect *billboard = node_effects->get_billboard();
   if (billboard != (const BillboardEffect *)NULL) {
   if (billboard != (const BillboardEffect *)NULL) {
     // Got to apply a billboard transform here.
     // Got to apply a billboard transform here.

+ 36 - 0
panda/src/pgraph/nodePath.I

@@ -885,6 +885,42 @@ get_distance(const NodePath &other) const {
   return length(LVector3f(pos));
   return length(LVector3f(pos));
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_billboard_axis
+//       Access: Published
+//  Description: Puts a billboard transition on the node such that it
+//               will rotate in two dimensions around the up axis.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_billboard_axis(float offset) {
+  set_billboard_axis(NodePath(), offset);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_billboard_point_eye
+//       Access: Published
+//  Description: Puts a billboard transition on the node such that it
+//               will rotate in three dimensions about the origin,
+//               keeping its up vector oriented to the top of the
+//               camera.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_billboard_point_eye(float offset) {
+  set_billboard_point_eye(NodePath(), offset);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_billboard_point_world
+//       Access: Published
+//  Description: Puts a billboard transition on the node such that it
+//               will rotate in three dimensions about the origin,
+//               keeping its up vector oriented to the sky.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_billboard_point_world(float offset) {
+  set_billboard_point_world(NodePath(), offset);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::adjust_all_priorities
 //     Function: NodePath::adjust_all_priorities
 //       Access: Published
 //       Access: Published

+ 51 - 9
panda/src/pgraph/nodePath.cxx

@@ -34,6 +34,7 @@
 #include "depthTestAttrib.h"
 #include "depthTestAttrib.h"
 #include "depthWriteAttrib.h"
 #include "depthWriteAttrib.h"
 #include "billboardEffect.h"
 #include "billboardEffect.h"
+#include "compassEffect.h"
 #include "showBoundsEffect.h"
 #include "showBoundsEffect.h"
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
 #include "materialPool.h"
 #include "materialPool.h"
@@ -2376,14 +2377,16 @@ do_billboard_point_world(const NodePath &camera, float offset) {
 //     Function: NodePath::set_billboard_axis
 //     Function: NodePath::set_billboard_axis
 //       Access: Published
 //       Access: Published
 //  Description: Puts a billboard transition on the node such that it
 //  Description: Puts a billboard transition on the node such that it
-//               will rotate in two dimensions around the up axis.
+//               will rotate in two dimensions around the up axis,
+//               towards a specified "camera" instead of to the
+//               viewing camera.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePath::
 void NodePath::
-set_billboard_axis(float offset) {
+set_billboard_axis(const NodePath &camera, float offset) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
   CPT(RenderEffect) billboard = BillboardEffect::make
   CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), false, true, 
     (LVector3f::up(), false, true, 
-     offset, NodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
+     offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
   node()->set_effect(billboard);
   node()->set_effect(billboard);
 }
 }
 
 
@@ -2393,14 +2396,15 @@ set_billboard_axis(float offset) {
 //  Description: Puts a billboard transition on the node such that it
 //  Description: Puts a billboard transition on the node such that it
 //               will rotate in three dimensions about the origin,
 //               will rotate in three dimensions about the origin,
 //               keeping its up vector oriented to the top of the
 //               keeping its up vector oriented to the top of the
-//               camera.
+//               camera, towards a specified "camera" instead of to
+//               the viewing camera.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePath::
 void NodePath::
-set_billboard_point_eye(float offset) {
+set_billboard_point_eye(const NodePath &camera, float offset) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
   CPT(RenderEffect) billboard = BillboardEffect::make
   CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), true, false,
     (LVector3f::up(), true, false,
-     offset, NodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
+     offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
   node()->set_effect(billboard);
   node()->set_effect(billboard);
 }
 }
 
 
@@ -2409,14 +2413,15 @@ set_billboard_point_eye(float offset) {
 //       Access: Published
 //       Access: Published
 //  Description: Puts a billboard transition on the node such that it
 //  Description: Puts a billboard transition on the node such that it
 //               will rotate in three dimensions about the origin,
 //               will rotate in three dimensions about the origin,
-//               keeping its up vector oriented to the sky.
+//               keeping its up vector oriented to the sky, towards a
+//               specified "camera" instead of to the viewing camera.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePath::
 void NodePath::
-set_billboard_point_world(float offset) {
+set_billboard_point_world(const NodePath &camera, float offset) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
   CPT(RenderEffect) billboard = BillboardEffect::make
   CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), false, false,
     (LVector3f::up(), false, false,
-     offset, NodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
+     offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
   node()->set_effect(billboard);
   node()->set_effect(billboard);
 }
 }
 
 
@@ -2443,6 +2448,43 @@ has_billboard() const {
   return node()->has_effect(BillboardEffect::get_class_type());
   return node()->has_effect(BillboardEffect::get_class_type());
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_compass
+//       Access: Published
+//  Description: Puts a compass effect on the node, so that it will
+//               retain a fixed rotation relative to the reference
+//               node (or render if the reference node is empty)
+//               regardless of the transforms above it.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_compass(const NodePath &reference) {
+  nassertv_always(!is_empty());
+  node()->set_effect(CompassEffect::make(reference));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_compass
+//       Access: Published
+//  Description: Removes any compass effect from the node.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_compass() {
+  nassertv_always(!is_empty());
+  node()->clear_effect(CompassEffect::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_compass
+//       Access: Published
+//  Description: Returns true if there is any compass effect on
+//               the node.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_compass() const {
+  nassertr_always(!is_empty(), false);
+  return node()->has_effect(CompassEffect::get_class_type());
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::set_transparency
 //     Function: NodePath::set_transparency
 //       Access: Published
 //       Access: Published

+ 10 - 3
panda/src/pgraph/nodePath.h

@@ -462,12 +462,19 @@ PUBLISHED:
   void do_billboard_axis(const NodePath &camera, float offset);
   void do_billboard_axis(const NodePath &camera, float offset);
   void do_billboard_point_eye(const NodePath &camera, float offset);
   void do_billboard_point_eye(const NodePath &camera, float offset);
   void do_billboard_point_world(const NodePath &camera, float offset);
   void do_billboard_point_world(const NodePath &camera, float offset);
-  void set_billboard_axis(float offset = 0.0);
-  void set_billboard_point_eye(float offset = 0.0);
-  void set_billboard_point_world(float offset = 0.0);
+  INLINE void set_billboard_axis(float offset = 0.0);
+  INLINE void set_billboard_point_eye(float offset = 0.0);
+  INLINE void set_billboard_point_world(float offset = 0.0);
+  void set_billboard_axis(const NodePath &camera, float offset);
+  void set_billboard_point_eye(const NodePath &camera, float offset);
+  void set_billboard_point_world(const NodePath &camera, float offset);
   void clear_billboard();
   void clear_billboard();
   bool has_billboard() const;
   bool has_billboard() const;
 
 
+  void set_compass(const NodePath &reference = NodePath());
+  void clear_compass();
+  bool has_compass() const;
+
   void set_transparency(bool transparency, int priority = 0);
   void set_transparency(bool transparency, int priority = 0);
   void clear_transparency();
   void clear_transparency();
   bool has_transparency() const;
   bool has_transparency() const;

+ 1 - 0
panda/src/pgraph/pgraph_composite1.cxx

@@ -8,6 +8,7 @@
 #include "colorBlendAttrib.cxx"
 #include "colorBlendAttrib.cxx"
 #include "colorScaleAttrib.cxx"
 #include "colorScaleAttrib.cxx"
 #include "colorWriteAttrib.cxx"
 #include "colorWriteAttrib.cxx"
+#include "compassEffect.cxx"
 #include "config_pgraph.cxx"
 #include "config_pgraph.cxx"
 #include "cullBin.cxx"
 #include "cullBin.cxx"
 #include "cullBinAttrib.cxx"
 #include "cullBinAttrib.cxx"

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

@@ -181,6 +181,25 @@ has_decal() const {
   return ((_flags & F_has_decal) != 0);
   return ((_flags & F_has_decal) != 0);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::get_compass
+//       Access: Public
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a CompassEffect on this state.  It returns a
+//               pointer to the CompassEffect, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const CompassEffect *RenderEffects::
+get_compass() const {
+  if ((_flags & F_checked_compass) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal compass cache.
+    ((RenderEffects *)this)->determine_compass();
+  }
+  return _compass;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffects::has_show_bounds
 //     Function: RenderEffects::has_show_bounds
 //       Access: Public
 //       Access: Public

+ 16 - 0
panda/src/pgraph/renderEffects.cxx

@@ -19,6 +19,7 @@
 #include "renderEffects.h"
 #include "renderEffects.h"
 #include "billboardEffect.h"
 #include "billboardEffect.h"
 #include "decalEffect.h"
 #include "decalEffect.h"
+#include "compassEffect.h"
 #include "showBoundsEffect.h"
 #include "showBoundsEffect.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
 #include "bamReader.h"
 #include "bamReader.h"
@@ -460,6 +461,21 @@ determine_decal() {
   _flags |= F_checked_decal;
   _flags |= F_checked_decal;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::determine_compass
+//       Access: Private
+//  Description: This is the private implementation of has_compass().
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+determine_compass() {
+  const RenderEffect *effect = get_effect(CompassEffect::get_class_type());
+  _compass = (const CompassEffect *)NULL;
+  if (effect != (const RenderEffect *)NULL) {
+    _compass = DCAST(CompassEffect, effect);
+  }
+  _flags |= F_checked_compass;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffects::determine_show_bounds
 //     Function: RenderEffects::determine_show_bounds
 //       Access: Private
 //       Access: Private

+ 5 - 0
panda/src/pgraph/renderEffects.h

@@ -28,6 +28,7 @@
 #include "ordered_vector.h"
 #include "ordered_vector.h"
 
 
 class BillboardEffect;
 class BillboardEffect;
+class CompassEffect;
 class FactoryParams;
 class FactoryParams;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -89,12 +90,14 @@ PUBLISHED:
 public:
 public:
   INLINE const BillboardEffect *get_billboard() const;
   INLINE const BillboardEffect *get_billboard() const;
   INLINE bool has_decal() const;
   INLINE bool has_decal() const;
+  INLINE const CompassEffect *get_compass() const;
   INLINE bool has_show_bounds() const;
   INLINE bool has_show_bounds() const;
 
 
 private:
 private:
   static CPT(RenderEffects) return_new(RenderEffects *state);
   static CPT(RenderEffects) return_new(RenderEffects *state);
   void determine_billboard();
   void determine_billboard();
   void determine_decal();
   void determine_decal();
+  void determine_compass();
   void determine_show_bounds();
   void determine_show_bounds();
 
 
 private:
 private:
@@ -129,6 +132,7 @@ private:
   // We cache the pointer to some critical effects stored in the
   // We cache the pointer to some critical effects stored in the
   // state, if they exist.
   // state, if they exist.
   const BillboardEffect *_billboard;
   const BillboardEffect *_billboard;
+  const CompassEffect *_compass;
 
 
   enum Flags {
   enum Flags {
     F_checked_billboard    = 0x0001,
     F_checked_billboard    = 0x0001,
@@ -136,6 +140,7 @@ private:
     F_has_decal            = 0x0004,
     F_has_decal            = 0x0004,
     F_checked_show_bounds  = 0x0008,
     F_checked_show_bounds  = 0x0008,
     F_has_show_bounds      = 0x0010,
     F_has_show_bounds      = 0x0010,
+    F_checked_compass      = 0x0020,
   };
   };
   short _flags;
   short _flags;
 
 

+ 22 - 8
panda/src/pgraph/sceneSetup.I

@@ -26,7 +26,7 @@ INLINE SceneSetup::
 SceneSetup() {
 SceneSetup() {
   _camera_transform = TransformState::make_identity();
   _camera_transform = TransformState::make_identity();
   _world_transform = TransformState::make_identity();
   _world_transform = TransformState::make_identity();
-  _render_transform = TransformState::make_identity();
+  _cs_transform = TransformState::make_identity();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -145,6 +145,7 @@ get_camera_transform() const {
 INLINE void SceneSetup::
 INLINE void SceneSetup::
 set_world_transform(const TransformState *world_transform) {
 set_world_transform(const TransformState *world_transform) {
   _world_transform = world_transform;
   _world_transform = world_transform;
+  _render_transform = _cs_transform->compose(_world_transform);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -160,16 +161,29 @@ get_world_transform() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SceneSetup::set_render_transform
+//     Function: SceneSetup::set_cs_transform
 //       Access: Public
 //       Access: Public
-//  Description: Specifies the position of the starting node relative
-//               to the camera, pretransformed as appropriate for
-//               rendering.  This is the same as the world transform,
-//               with a possible coordinate-system conversion applied.
+//  Description: Specifies the pretransformation to apply to the world
+//               transform to produce the appropriate transformation
+//               for rendering.  This is usually the appropriate
+//               coordinate-system conversion for the current GSG.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
 INLINE void SceneSetup::
-set_render_transform(const TransformState *render_transform) {
-  _render_transform = render_transform;
+set_cs_transform(const TransformState *cs_transform) {
+  _cs_transform = cs_transform;
+  _render_transform = _cs_transform->compose(_world_transform);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_cs_transform
+//       Access: Public
+//  Description: Returns the pretransformation to apply to the world
+//               transform to produce the appropriate transformation
+//               for rendering.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *SceneSetup::
+get_cs_transform() const {
+  return _cs_transform;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 4 - 1
panda/src/pgraph/sceneSetup.h

@@ -56,7 +56,9 @@ public:
   INLINE void set_world_transform(const TransformState *world_transform);
   INLINE void set_world_transform(const TransformState *world_transform);
   INLINE const TransformState *get_world_transform() const;
   INLINE const TransformState *get_world_transform() const;
 
 
-  INLINE void set_render_transform(const TransformState *render_transform);
+  INLINE void set_cs_transform(const TransformState *cs_transform);
+  INLINE const TransformState *get_cs_transform() const;
+
   INLINE const TransformState *get_render_transform() const;
   INLINE const TransformState *get_render_transform() const;
 
 
 private:
 private:
@@ -66,6 +68,7 @@ private:
   CPT(Lens) _lens;
   CPT(Lens) _lens;
   CPT(TransformState) _camera_transform;
   CPT(TransformState) _camera_transform;
   CPT(TransformState) _world_transform;
   CPT(TransformState) _world_transform;
+  CPT(TransformState) _cs_transform;
   CPT(TransformState) _render_transform;
   CPT(TransformState) _render_transform;
 };
 };