Browse Source

Support collision solids under LOD nodes

David Rose 21 years ago
parent
commit
d850afd71a

+ 9 - 0
panda/src/collide/collisionTraverser.cxx

@@ -27,6 +27,7 @@
 #include "transformState.h"
 #include "geomNode.h"
 #include "geom.h"
+#include "lodNode.h"
 #include "nodePath.h"
 #include "pStatTimer.h"
 #include "indent.h"
@@ -597,6 +598,14 @@ r_traverse(CollisionLevelState &level_state) {
       r_traverse(next_state);
     }
 
+  } else if (node->is_lod_node()) {
+    // If it's an LODNode, visit the lowest level of detail.
+    int index = DCAST(LODNode, node)->get_lowest_switch();
+    if (index >= 0 && index < node->get_num_children()) {
+      CollisionLevelState next_state(level_state, node->get_child(index));
+      r_traverse(next_state);
+    }
+
   } else {
     // Otherwise, visit all the children.
     int num_children = node->get_num_children();

+ 1 - 1
panda/src/framework/pandaFramework.cxx

@@ -447,7 +447,7 @@ all_windows_closed() const {
 //               be instanced to each window's render tree as the
 //               window is created.
 ////////////////////////////////////////////////////////////////////
-const NodePath &PandaFramework::
+NodePath &PandaFramework::
 get_models() {
   if (_models.is_empty()) {
     _models = NodePath("models");

+ 1 - 1
panda/src/framework/pandaFramework.h

@@ -77,7 +77,7 @@ public:
   void close_all_windows();
   bool all_windows_closed() const;
 
-  const NodePath &get_models();
+  NodePath &get_models();
 
   void report_frame_rate(ostream &out) const;
   void reset_frame_rate();

+ 0 - 184
panda/src/gobj/LOD.I

@@ -1,184 +0,0 @@
-// Filename: LOD.I
-// Created by:  mike (09Jan97)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include <math.h>
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::Default Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODSwitch::
-LODSwitch() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODSwitch::
-LODSwitch(float in, float out) {
-  set_range(in, out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODSwitch::
-LODSwitch(const LODSwitch &copy) :
-  _in(copy._in),
-  _out(copy._out)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::Copy Assignment Operator
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-operator = (const LODSwitch &copy) {
-  _in = copy._in;
-  _out = copy._out;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::get_range
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-get_range(float &in, float &out) const {
-  in = get_in();
-  out = get_out();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::get_in
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float LODSwitch::
-get_in() const {
-  return sqrtf(_in);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::get_out
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float LODSwitch::
-get_out() const {
-  return sqrtf(_out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::set_range
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-set_range(float in, float out) {
-  // We actually store the square of the switching distances.  This
-  // makes the LOD computation a little simpler.
-  _in = in * in;
-  _out = out * out;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::in_range
-//       Access: Public
-//  Description: Computes the distance between two points and returns
-//               true if the result is within the range for the LOD.
-////////////////////////////////////////////////////////////////////
-INLINE bool LODSwitch::
-in_range(float dist_squared) const {
-  return (dist_squared >= _out && dist_squared < _in);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::rescale
-//       Access: Public
-//  Description: Scales the switching distances by the square root of
-//               the indicated factor.
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-rescale(float factor_squared) {
-  _in *= factor_squared;
-  _out *= factor_squared;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::operator ==
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool LODSwitch::
-operator == (const LODSwitch &) const {
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::operator !=
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool LODSwitch::
-operator != (const LODSwitch &) const {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::operator <
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool LODSwitch::
-operator < (const LODSwitch &) const {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::write_datagram
-//       Access: Public
-//  Description: Writes the contents of the LODSwitch out to the
-//               datagram, presumably in preparation to writing to a
-//               Bam file.
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-write_datagram(Datagram &destination) const {
-  destination.add_float32(_in);
-  destination.add_float32(_out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODSwitch::read_datagram
-//       Access: Public
-//  Description: Reads the contents of the LODSwitch from the
-//               datagram, presumably in response to reading a Bam
-//               file.
-////////////////////////////////////////////////////////////////////
-INLINE void LODSwitch::
-read_datagram(DatagramIterator &source) {
-  _in = source.get_float32();
-  _out = source.get_float32();
-}

+ 0 - 228
panda/src/gobj/LOD.cxx

@@ -1,228 +0,0 @@
-// Filename: LOD.cxx
-// Created by:  mike (09Jan97)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-#include "LOD.h"
-
-#include "datagram.h"
-#include "datagramIterator.h"
-#include "indent.h"
-#include "config_gobj.h"
-
-#define EXPCL EXPCL_PANDA
-#define EXPTP EXPTP_PANDA
-#define TYPE LODSwitch
-#define NAME LODSwitchVector
-
-#include "vector_src.cxx"
-
-float LOD::_stress_factor = lod_stress_factor;
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-LOD::
-LOD(void) {
-  _center.set(0.0f, 0.0f, 0.0f);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-LOD::
-LOD(const LOD &copy) :
-  _center(copy._center),
-  _switch_vector(copy._switch_vector)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::destructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-LOD::
-~LOD(void) {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::xform
-//       Access: Public
-//  Description: Transforms the LOD specification by the indicated
-//               matrix.
-////////////////////////////////////////////////////////////////////
-void LOD::
-xform(const LMatrix4f &mat) {
-  _center = _center * mat;
-
-  // We'll take just the length of the y axis as the matrix's scale.
-  LVector3f y;
-  mat.get_row3(y,1);
-  float factor_squared = y.length_squared();
-
-  LODSwitchVector::iterator si;
-  for (si = _switch_vector.begin(); si != _switch_vector.end(); ++si) {
-    (*si).rescale(factor_squared);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::compute_child
-//       Access: Public
-//  Description: Computes the distance between two points and returns
-//               the index for the child of the LOD by testing against
-//               the corresponding list of switching distances.
-////////////////////////////////////////////////////////////////////
-int LOD::
-compute_child(const LPoint3f &cam_pos, const LPoint3f &center) const {
-
-  LVector3f v = cam_pos - center;
-  float dist = dot(v, v) * _stress_factor;
-  LODSwitchVector::const_iterator i;
-  int child = 0;
-  for (i = _switch_vector.begin(), child = 0;
-       i != _switch_vector.end(); ++i, ++child) {
-    if ((*i).in_range(dist))
-      break;
-  }
-
-  if(debug_LOD_mode) {
-      //not ifndef NDEBUG'ing this out since need it at Opt4 for perf measurements
-      if(select_LOD_number>=0) {
-          return select_LOD_number;
-      }
-
-      // since lowest level LOD is lev 0, must invert the meaning of
-      // so minimum_LOD_number 0 will screen out no LODs, and increasing it
-      // will screen out successively higher levels
-      int max_allowed_LOD_number = _switch_vector.size() - minimum_LOD_number;
-      if(child > max_allowed_LOD_number) {
-          child = max_allowed_LOD_number;
-      }
-  }
-
-  return child;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::write_datagram
-//       Access: Public
-//  Description: Writes the contents of the LOD out to the datagram,
-//               presumably in preparation to writing to a Bam file.
-////////////////////////////////////////////////////////////////////
-void LOD::
-write_datagram(Datagram &destination) const {
-  _center.write_datagram(destination);
-
-  destination.add_uint16(_switch_vector.size());
-
-  LODSwitchVector::const_iterator si;
-  for (si = _switch_vector.begin();
-       si != _switch_vector.end();
-       ++si) {
-    (*si).write_datagram(destination);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::read_datagram
-//       Access: Public
-//  Description: Reads the contents of the LOD from the datagram,
-//               presumably in response to reading a Bam file.
-////////////////////////////////////////////////////////////////////
-void LOD::
-read_datagram(DatagramIterator &source) {
-  _center.read_datagram(source);
-
-  _switch_vector.clear();
-
-  int num_switches = source.get_uint16();
-  _switch_vector.reserve(num_switches);
-  for (int i = 0; i < num_switches; i++) {
-    _switch_vector.push_back(LODSwitch(0, 0));
-    _switch_vector.back().read_datagram(source);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::output
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void LOD::
-output(ostream &out) const {
-  if (_switch_vector.empty()) {
-    out << "no switches.";
-  } else {
-    LODSwitchVector::const_iterator si;
-    si = _switch_vector.begin();
-    out << "(" << (*si).get_in() << "/" << (*si).get_out() << ")";
-    ++si;
-    while (si != _switch_vector.end()) {
-      out << " (" << (*si).get_in() << "/" << (*si).get_out() << ")";
-      ++si;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::write
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void LOD::
-write(ostream &out, int indent_level) const {
-  indent(out, indent_level)
-    << "LOD, " << _switch_vector.size() << " switches:\n";
-  LODSwitchVector::const_iterator si;
-  int i = 0;
-  for (si = _switch_vector.begin();
-       si != _switch_vector.end();
-       ++si) {
-    indent(out, indent_level + 2)
-      << i << ". in at " << (*si).get_in()
-      << ", out at " << (*si).get_out() << "\n";
-    i++;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::set_stress_factor
-//       Access: Published, Static
-//  Description: Sets the factor that globally scales all LOD's.  This
-//               factor is applied to the square of the LOD distance,
-//               so the larger the number, the lower the detail that
-//               is presented.  The normal value is 1.0.
-////////////////////////////////////////////////////////////////////
-void LOD::
-set_stress_factor(float stress_factor) {
-  _stress_factor = stress_factor;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LOD::get_stress_factor
-//       Access: Published, Static
-//  Description: Returns the factor that globally scales all LOD's.
-//               See get_stress_factor().
-////////////////////////////////////////////////////////////////////
-float LOD::
-get_stress_factor() {
-  return _stress_factor;
-}

+ 0 - 119
panda/src/gobj/LOD.h

@@ -1,119 +0,0 @@
-// Filename: LOD.h
-// Created by:  mike (09Jan97)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef LOD_H
-#define LOD_H
-
-#include "pandabase.h"
-
-#include "luse.h"
-#include "typedReferenceCount.h"
-
-class Datagram;
-class DatagramIterator;
-
-////////////////////////////////////////////////////////////////////
-//       Class : LODSwitch
-// Description : Defines a switching region for an LOD.  An object
-//               will be visible when it is closer than "in" units,
-//               but further than "out" units from the camera.
-//
-//               The sense of in vs. out distances is as if the object
-//               were coming towards you from far away: it switches
-//               "in" at the far distance, and switches "out" at the
-//               close distance.  Thus, "in" should be larger than
-//               "out".
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA LODSwitch {
-public:
-  INLINE LODSwitch();
-  INLINE LODSwitch(float in, float out);
-  INLINE LODSwitch(const LODSwitch &copy);
-  INLINE void operator = (const LODSwitch &copy);
-
-  INLINE void get_range(float &in, float &out) const;
-  INLINE float get_in() const;
-  INLINE float get_out() const;
-  INLINE void set_range(float in, float out);
-  INLINE bool in_range(float dist_squared) const;
-
-  INLINE void rescale(float factor_squared);
-
-  // We must declare these operators to allow VC++ to explicitly
-  // export pvector<LODSwitch>, below.  They don't do anything useful.
-  INLINE bool operator == (const LODSwitch &other) const;
-  INLINE bool operator != (const LODSwitch &other) const;
-  INLINE bool operator < (const LODSwitch &other) const;
-
-  INLINE void write_datagram(Datagram &destination) const;
-  INLINE void read_datagram(DatagramIterator &source);
-
-protected:
-  float _in;
-  float _out;
-};
-
-#define EXPCL EXPCL_PANDA
-#define EXPTP EXPTP_PANDA
-#define TYPE LODSwitch
-#define NAME LODSwitchVector
-
-#include "vector_src.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : LOD
-// Description : Computes whether a level-of-detail should be rendered
-//               or not based on distance from the rendering camera.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA LOD {
-public:
-  LOD();
-  LOD(const LOD &copy);
-  ~LOD();
-
-  void xform(const LMatrix4f &mat);
-
-  int compute_child(const LPoint3f &cam_pos,
-                    const LPoint3f &center) const;
-
-  void write_datagram(Datagram &destination) const;
-  void read_datagram(DatagramIterator &source);
-
-  void output(ostream &out) const;
-  void write(ostream &out, int indent_level = 0) const;
-
-PUBLISHED:
-  static void set_stress_factor(float stress_factor);
-  static float get_stress_factor();
-
-public:
-  LPoint3f _center;
-  LODSwitchVector _switch_vector;
-
-private:
-  static float _stress_factor;
-};
-
-INLINE ostream &operator << (ostream &out, const LOD &lod) {
-  lod.output(out);
-  return out;
-}
-
-#include "LOD.I"
-
-#endif

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

@@ -10,7 +10,6 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES \
-    LOD.I LOD.h \
     boundedObject.I boundedObject.h \
     config_gobj.h drawable.h \
     geom.I geom.h \
@@ -33,7 +32,6 @@
     textureStage.I textureStage.h
     
   #define INCLUDED_SOURCES \
-    LOD.cxx \
     boundedObject.cxx \
     config_gobj.cxx drawable.cxx geom.cxx  \
     geomContext.cxx \
@@ -48,7 +46,6 @@
     texCoordName.cxx textureStage.cxx
 
   #define INSTALL_HEADERS \
-    LOD.I LOD.h \
     boundedObject.I boundedObject.h \
     config_gobj.h \
     drawable.h geom.I geom.h \

+ 0 - 14
panda/src/gobj/config_gobj.cxx

@@ -107,20 +107,6 @@ BamTextureMode bam_texture_mode;
 // texture file, which will presumably only be loaded once.
 const string fake_texture_image = config_gobj.GetString("fake-texture-image", "");
 
-// must be set to true for LOD_number debugging items to work
-const bool debug_LOD_mode = config_gobj.GetBool("debug-LOD-mode", false);
-
-// if this is >=0, select_child always returns this LOD number
-const int select_LOD_number = config_gobj.GetInt("select-LOD-number", -1);
-
-// this controls the LOD child number returned by compute_child
-// use it to force the LOD alg to not select the highest level(s) of LOD
-// minimum_LOD_number=0 will screen out no LODs, and increasing it
-// will screen out successively higher levels
-const int minimum_LOD_number = config_gobj.GetInt("minimum-LOD-number", 0);
-
-const float lod_stress_factor = config_gobj.GetFloat("lod-stress-factor", 1.0f);
-
 // The default near and far plane distances.
 const float default_near = config_gobj.GetFloat("default-near", 1.0f);
 const float default_far = config_gobj.GetFloat("default-far", 1000.0f);

+ 0 - 5
panda/src/gobj/config_gobj.h

@@ -44,11 +44,6 @@ enum BamTextureMode {
 extern EXPCL_PANDA BamTextureMode bam_texture_mode;
 extern EXPCL_PANDA const string fake_texture_image;
 
-extern EXPCL_PANDA const bool debug_number_mode;
-extern EXPCL_PANDA const int select_LOD_number;
-extern EXPCL_PANDA const int minimum_LOD_number;
-extern EXPCL_PANDA const bool debug_LOD_mode;
-
 extern const float default_near;
 extern const float default_far;
 extern const float default_fov;

+ 0 - 1
panda/src/gobj/gobj_composite2.cxx

@@ -3,7 +3,6 @@
 #include "drawable.cxx"
 #include "geomContext.cxx"
 #include "imageBuffer.cxx"
-#include "LOD.cxx"
 #include "material.cxx"
 #include "materialPool.cxx"
 #include "orthographicLens.cxx"

+ 8 - 144
panda/src/pgraph/fadeLodNode.I

@@ -16,27 +16,6 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE FadeLODNode::CData::
-CData() {
-  _fade_time = 1.0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE FadeLODNode::CData::
-CData(const FadeLODNode::CData &copy) :
-  _lod(copy._lod) {
-  _fade_time = copy._fade_time;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: FadeLODNode::Constructor
 //       Access: Published
@@ -44,7 +23,9 @@ CData(const FadeLODNode::CData &copy) :
 ////////////////////////////////////////////////////////////////////
 INLINE FadeLODNode::
 FadeLODNode(const string &name) :
-  PandaNode(name) {
+  LODNode(name) 
+{
+  _fade_time = 1.0;
   _fade_mode = false;
   _previous_child = 0;
 }
@@ -56,128 +37,13 @@ FadeLODNode(const string &name) :
 ////////////////////////////////////////////////////////////////////
 INLINE FadeLODNode::
 FadeLODNode(const FadeLODNode &copy) :
-  PandaNode(copy),
-  _cycler(copy._cycler) {
+  LODNode(copy)
+{
+  _fade_time = copy._fade_time;
   _fade_mode = copy._fade_mode;
   _previous_child = copy._previous_child;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::add_switch
-//       Access: Published
-//  Description: Adds a switch range to the FadeLODNode.  This implies
-//               that the corresponding child node has been parented
-//               to the node.
-//
-//               The sense of in vs. out distances is as if the object
-//               were coming towards you from far away: it switches
-//               "in" at the far distance, and switches "out" at the
-//               close distance.  Thus, "in" should be larger than
-//               "out".
-////////////////////////////////////////////////////////////////////
-INLINE void FadeLODNode::
-add_switch(float in, float out) {
-  CDWriter cdata(_cycler);
-  cdata->_lod._switch_vector.push_back(LODSwitch(in, out));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::set_switch
-//       Access: Published
-//  Description: Changes the switching range of a particular child of
-//               the LODNode.  See add_switch().
-////////////////////////////////////////////////////////////////////
-INLINE bool FadeLODNode::
-set_switch(int index, float in, float out) {
-  CDWriter cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), false);
-  cdata->_lod._switch_vector[index].set_range(in, out);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::clear_switches
-//       Access: Published
-//  Description: Removes the set of switching ranges for the LODNode,
-//               presumably in conjunction with removing all of its
-//               children.  See add_switch().
-////////////////////////////////////////////////////////////////////
-INLINE void FadeLODNode::
-clear_switches(void) {
-  CDWriter cdata(_cycler);
-  cdata->_lod._switch_vector.erase(cdata->_lod._switch_vector.begin(),
-                                   cdata->_lod._switch_vector.end());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::get_num_switches
-//       Access: Published
-//  Description: Returns the number of switch ranges added to the
-//               LODNode.  This should correspond to the number of
-//               children of the node in order for the FadeLODNode to
-//               function correctly.
-////////////////////////////////////////////////////////////////////
-INLINE int FadeLODNode::
-get_num_switches() const {
-  CDReader cdata(_cycler);
-  return cdata->_lod._switch_vector.size();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::get_in
-//       Access: Published
-//  Description: Returns the "in" distance of the indicated switch
-//               range.  This should be larger than the "out" distance
-//               of the same range.
-////////////////////////////////////////////////////////////////////
-INLINE float FadeLODNode::
-get_in(int index) const {
-  CDReader cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
-  return cdata->_lod._switch_vector[index].get_in();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::get_out
-//       Access: Published
-//  Description: Returns the "out" distance of the indicated switch
-//               range.  This should be smaller than the "in" distance
-//               of the same range.
-////////////////////////////////////////////////////////////////////
-INLINE float FadeLODNode::
-get_out(int index) const {
-  CDReader cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
-  return cdata->_lod._switch_vector[index].get_out();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::set_center
-//       Access: Published
-//  Description: Specifies the center of the LOD.  This is the point
-//               that is compared to the camera (in camera space) to
-//               determine the particular LOD that should be chosen.
-////////////////////////////////////////////////////////////////////
-INLINE void FadeLODNode::
-set_center(const LPoint3f &center) {
-  CDWriter cdata(_cycler);
-  cdata->_lod._center = center;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::get_center
-//       Access: Published
-//  Description: Returns the center of the LOD.  This is the point
-//               that is compared to the camera (in camera space) to
-//               determine the particular LOD that should be chosen.
-////////////////////////////////////////////////////////////////////
-INLINE const LPoint3f &FadeLODNode::
-get_center() const {
-  CDReader cdata(_cycler);
-  return cdata->_lod._center;
-}
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: FadeLODNode::set_fade_time
 //       Access: Published
@@ -185,8 +51,7 @@ get_center() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void FadeLODNode::
 set_fade_time(float t) {
-  CDWriter cdata(_cycler);
-  cdata->_fade_time = t;
+  _fade_time = t;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -196,7 +61,6 @@ set_fade_time(float t) {
 ////////////////////////////////////////////////////////////////////
 INLINE float FadeLODNode::
 get_fade_time() const {
-  CDReader cdata(_cycler);
-  return cdata->_fade_time;
+  return _fade_time;
 }
 

+ 95 - 159
panda/src/pgraph/fadeLodNode.cxx

@@ -26,39 +26,6 @@
 
 TypeHandle FadeLODNode::_type_handle;
 
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *FadeLODNode::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void FadeLODNode::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  _lod.write_datagram(dg);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new LODNode.
-////////////////////////////////////////////////////////////////////
-void FadeLODNode::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  _lod.read_datagram(scan);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: FadeLODNode::make_copy
 //       Access: Public, Virtual
@@ -72,48 +39,6 @@ make_copy() const {
   return new FadeLODNode(*this);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::safe_to_combine
-//       Access: Public, Virtual
-//  Description: Returns true if it is generally safe to combine this
-//               particular kind of PandaNode with other kinds of
-//               PandaNodes, adding children or whatever.  For
-//               instance, an LODNode should not be combined with any
-//               other PandaNode, because its set of children is
-//               meaningful.
-////////////////////////////////////////////////////////////////////
-bool FadeLODNode::
-safe_to_combine() const {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::xform
-//       Access: Public, Virtual
-//  Description: Transforms the contents of this PandaNode by the
-//               indicated matrix, if it means anything to do so.  For
-//               most kinds of PandaNodes, this does nothing.
-////////////////////////////////////////////////////////////////////
-void FadeLODNode::
-xform(const LMatrix4f &mat) {
-  CDWriter cdata(_cycler);
-  cdata->_lod.xform(mat);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FadeLODNode::has_cull_callback
-//       Access: Public, Virtual
-//  Description: Should be overridden by derived classes to return
-//               true if cull_callback() has been defined.  Otherwise,
-//               returns false to indicate cull_callback() does not
-//               need to be called for this node during the cull
-//               traversal.
-////////////////////////////////////////////////////////////////////
-bool FadeLODNode::
-has_cull_callback() const {
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: FadeLODNode::cull_callback
 //       Access: Public, Virtual
@@ -135,94 +60,90 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 bool FadeLODNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
-  PandaNode *node = data.node();
-  CDReader cdata(_cycler);
   if (_fade_mode) {
-    float in_alpha;
-    float out_alpha;
-    _fade_timer -= ClockObject::get_global_clock()->get_dt();
-      if (_fade_timer <= (cdata->_fade_time / 2.0)) { 
-        //SECOND HALF OF FADE:
-        //Fade out the old LOD with z write off and 
-        //draw the opaque new LOD with z write on
-        out_alpha = (_fade_timer*2.0) / cdata->_fade_time;  
-        if (out_alpha < 0.0) {
-          out_alpha = 0.0;
-        }
-        
-        CullTraverserData next_data_in(data, node->get_child(_fade_in));
-        CullTraverserData next_data_out(data, node->get_child(_fade_out));
-        
-        // Disable Transparency on new LOD
-        next_data_in._state = next_data_in._state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_none), 0);
-        // Enable Transparency on old LOD
-        next_data_out._state = next_data_out._state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha), 0);
-        // Start Fading the old LOD out
-        next_data_out._state = next_data_out._state->add_attrib(ColorScaleAttrib::make(LVecBase4f(1.0,1.0,1.0,out_alpha)));
-        // The new LOD is now opaque and has depth writing
-        next_data_in._state = next_data_in._state->add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on), 0);
-        // The old LOD is fading so it doesnt depth write
-        next_data_out._state = next_data_out._state->add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off), 0);
-        
-        
-        trav->traverse(next_data_in);
+    float now = ClockObject::get_global_clock()->get_frame_time();
+    float elapsed = now - _fade_start;
+
+    float half_fade_time = _fade_time / 2.0f;
+
+    if (elapsed < half_fade_time) { 
+      // FIRST HALF OF FADE
+      // Fade the new LOD in with z writing off
+      // Keep drawing the old LOD opaque with z writing on
+      if (_fade_out >= 0 && _fade_out < get_num_children()) {
+        CullTraverserData next_data_out(data, get_child(_fade_out));
         trav->traverse(next_data_out);
-      } else {
-        // FIRST HALF OF FADE
-        // Fade the new LOD in with z writing off
-        // Keep drawing the old LOD opaque with z writing on
-        in_alpha = (1.0 - (_fade_timer / cdata->_fade_time))*2.0;  
-         if (in_alpha > 1.0) {
-          in_alpha = 1.0;
+      }
+
+      if (_fade_in >= 0 && _fade_in < get_num_children()) {
+        CullTraverserData next_data_in(data, get_child(_fade_in));
+
+        float in_alpha = elapsed / half_fade_time;
+        if (in_alpha > 1.0f) {
+          in_alpha = 1.0f;
         }
-        
-        CullTraverserData next_data_out(data, node->get_child(_fade_out));
-        CullTraverserData next_data_in(data, node->get_child(_fade_in));
+        LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, in_alpha);
+
+        next_data_in._state = 
+          next_data_in._state->compose(get_fade_out_state())->compose
+          (RenderState::make(ColorScaleAttrib::make(alpha_scale)));
+
+        trav->traverse(next_data_in);
+      }
 
-        // Disable transparency on old LOD
-        next_data_out._state = next_data_out._state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_none), 0);
-        // Enable transparency on new LOD
-        next_data_in._state = next_data_in._state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha), 0);
-        // Start Fading in the new LOD
-        next_data_in._state = next_data_in._state->add_attrib(ColorScaleAttrib::make(LVecBase4f(1.0,1.0,1.0,in_alpha)));
-        // Enable depth write for the old LOD
-        next_data_out._state = next_data_out._state->add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on), 0);
-        // Disable depth write for the new LOD
-        next_data_in._state = next_data_in._state->add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off), 0);
+    } else if (elapsed < _fade_time) {
+      //SECOND HALF OF FADE:
+      //Fade out the old LOD with z write off and 
+      //draw the opaque new LOD with z write on
+      if (_fade_in >= 0 && _fade_in < get_num_children()) {
+        CullTraverserData next_data_in(data, get_child(_fade_in));
+        trav->traverse(next_data_in);
+      }
+
+      if (_fade_out >= 0 && _fade_out < get_num_children()) {
+        CullTraverserData next_data_out(data, get_child(_fade_out));
         
+        float out_alpha = 1.0f - elapsed / half_fade_time;  
+        if (out_alpha < 0.0f) {
+          out_alpha = 0.0f;
+        }
+        LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, out_alpha);
+
+        next_data_out._state = 
+          next_data_out._state->compose(get_fade_out_state())->compose
+          (RenderState::make(ColorScaleAttrib::make(alpha_scale)));
+
         trav->traverse(next_data_out);
-        trav->traverse(next_data_in);
       }
-    if (_fade_timer < 0) { // Fading Complete
+
+    } else {
+      // Fading complete
       _fade_mode = false;
+      
+      if (_fade_in >= 0 && _fade_in < get_num_children()) {
+        CullTraverserData next_data_in(data, get_child(_fade_in));
+        trav->traverse(next_data_in);
+      }
     }
+
   } else {
-    if (data._net_transform->is_singular()) {
-      // If we're under a singular transform, we can't compute the LOD;
-      // select none of them instead.
-      //select_child(get_num_children());
-      return false;
-    } else { 
-      LPoint3f camera_pos(0, 0, 0);
-      // Get the LOD center in camera space
-      CPT(TransformState) rel_transform =
-        trav->get_camera_transform()->invert_compose(data._net_transform);
-      LPoint3f center = cdata->_lod._center * rel_transform->get_mat();
-      // Determine which child to traverse 
-      int index = cdata->_lod.compute_child(camera_pos, center);
-      //printf("CHILD: %d PREVIOUS %d \n",index,_previous_child);
-      if (index != _previous_child) { // Transition occurred
-        _fade_mode = true;
-        _fade_timer = cdata->_fade_time;
-        _fade_out = _previous_child; 
-        _fade_in = index;
-        _previous_child = index;
-        CullTraverserData next_data_transition(data, node->get_child(_fade_out));
+    int index = compute_child(trav, data);
+    if (index != _previous_child) { // Transition occurred
+      _fade_mode = true;
+      _fade_start = ClockObject::get_global_clock()->get_frame_time();
+      _fade_out = _previous_child; 
+      _fade_in = index;
+      _previous_child = index;
+      if (_fade_out >= 0 && _fade_out < get_num_children()) {
+        CullTraverserData next_data_transition(data, get_child(_fade_out));
         trav->traverse(next_data_transition);
-      } else {
+      }
+      
+    } else {
+      if (index >= 0 && index < get_num_children()) {
         // No transition... handle things as usual
         // Traverse only one valid child
-        CullTraverserData next_data_normal(data, node->get_child(index));
+        CullTraverserData next_data_normal(data, get_child(index));
         trav->traverse(next_data_normal);
       }
     }
@@ -238,11 +159,28 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 void FadeLODNode::
 output(ostream &out) const {
- PandaNode::output(out);
-  CDReader cdata(_cycler);
-  out << " ";
-  cdata->_lod.output(out);
-  out<< "Fade Time : " << cdata->_fade_time << endl;
+  LODNode::output(out);
+  out << " fade time: " << _fade_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FadeLODNode::get_fade_out_state
+//       Access: Protected, Static
+//  Description: Returns a RenderState for rendering the element that
+//               is switching out of visibility.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) FadeLODNode::
+get_fade_out_state() {
+  // Once someone asks for this pointer, we hold its reference count
+  // and never free it.
+  static CPT(RenderState) state = (const RenderState *)NULL;
+  if (state == (const RenderState *)NULL) {
+    state = RenderState::make
+      (TransparencyAttrib::make(TransparencyAttrib::M_alpha),
+       DepthWriteAttrib::make(DepthWriteAttrib::M_off));
+  }
+
+  return state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -264,8 +202,7 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void FadeLODNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  PandaNode::write_datagram(manager, dg);
-  manager->write_cdata(dg, _cycler);
+  LODNode::write_datagram(manager, dg);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -297,6 +234,5 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void FadeLODNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  PandaNode::fillin(scan, manager);
-  manager->read_cdata(scan, _cycler);
+  LODNode::fillin(scan, manager);
 }

+ 16 - 46
panda/src/pgraph/fadeLodNode.h

@@ -21,80 +21,50 @@
 
 #include "pandabase.h"
 
-#include "pandaNode.h"
-
-#include "LOD.h"
+#include "lodNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : FadeLODNode
 // Description : A Level-of-Detail node with alpha based switching.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA FadeLODNode : public PandaNode {
+class EXPCL_PANDA FadeLODNode : public LODNode {
 PUBLISHED:
   INLINE FadeLODNode(const string &name);
 
-
 protected:
   INLINE FadeLODNode(const FadeLODNode &copy);
 public:
   virtual PandaNode *make_copy() const;
-  virtual bool safe_to_combine() const;
-  virtual void xform(const LMatrix4f &mat);
-  virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual void output(ostream &out) const;
 
 PUBLISHED:
-  // The sense of in vs. out distances is as if the object were coming
-  // towards you from far away: it switches "in" at the far distance,
-  // and switches "out" at the close distance.  Thus, "in" should be
-  // larger than "out".
-
-  INLINE void add_switch(float in, float out);
-  INLINE bool set_switch(int index, float in, float out);
-  INLINE void clear_switches(void);
-
-  INLINE int get_num_switches() const;
-  INLINE float get_in(int index) const;
-  INLINE float get_out(int index) const;
-
-  INLINE void set_center(const LPoint3f &center);
-  INLINE const LPoint3f &get_center() const;
-
   INLINE void set_fade_time(float t);
   INLINE float get_fade_time() const;
 
-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);
-
 private:
-  class EXPCL_PANDA CData : public CycleData {
-  public:
-    INLINE CData();
-    INLINE CData(const CData &copy);
-    virtual CycleData *make_copy() const;
-    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+  static CPT(RenderState) get_fade_out_state();
 
-    LOD _lod;
-    float _fade_time;
-  };
+private:
+  float _fade_time;
 
-  PipelineCycler<CData> _cycler;
-  typedef CycleDataReader<CData> CDReader;
-  typedef CycleDataWriter<CData> CDWriter;
+  // These values are used to implement the fade-in-transition, and
+  // really shouldn't be stored here (because it is incompatible with
+  // multiple cameras on the scene, or multiple instances of the
+  // LODNode).
   bool _fade_mode;
-  float _fade_timer;
+  float _fade_start;
   int _fade_out;
   int _fade_in;
   int _previous_child;
   
+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() {

+ 176 - 16
panda/src/pgraph/lodNode.I

@@ -16,13 +16,20 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::CData::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE LODNode::CData::
-CData() {
+CData() :
+  _center(0.0f, 0.0f, 0.0f),
+  _lowest(0),
+  _highest(0),
+  _got_force_switch(false),
+  _force_switch(0)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -32,10 +39,105 @@ CData() {
 ////////////////////////////////////////////////////////////////////
 INLINE LODNode::CData::
 CData(const LODNode::CData &copy) :
-  _lod(copy._lod)
+  _center(copy._center),
+  _switch_vector(copy._switch_vector),
+  _lowest(copy._lowest),
+  _highest(copy._highest),
+  _got_force_switch(copy._got_force_switch),
+  _force_switch(copy._force_switch)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE LODNode::Switch::
+Switch(float in, float out) {
+  set_range(in, out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_in
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float LODNode::Switch::
+get_in() const {
+  return _in;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_out
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float LODNode::Switch::
+get_out() const {
+  return _out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::set_range
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+set_range(float in, float out) {
+  _in = in;
+  _out = out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::in_range
+//       Access: Public
+//  Description: Computes the distance between two points and returns
+//               true if the result is within the range for the LOD.
+////////////////////////////////////////////////////////////////////
+INLINE bool LODNode::Switch::
+in_range(float dist) const {
+  return (dist >= _out && dist < _in);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::rescale
+//       Access: Public
+//  Description: Scales the switching distances by the indicated factor.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+rescale(float factor) {
+  _in *= factor;
+  _out *= factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::write_datagram
+//       Access: Public
+//  Description: Writes the contents of the Switch out to the
+//               datagram, presumably in preparation to writing to a
+//               Bam file.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+write_datagram(Datagram &destination) const {
+  destination.add_float32(_in);
+  destination.add_float32(_out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::read_datagram
+//       Access: Public
+//  Description: Reads the contents of the Switch from the datagram,
+//               presumably in response to reading a Bam file.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+read_datagram(DatagramIterator &source) {
+  _in = source.get_float32();
+  _out = source.get_float32();
+}
+
+
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::Constructor
 //       Access: Published
@@ -43,7 +145,7 @@ CData(const LODNode::CData &copy) :
 ////////////////////////////////////////////////////////////////////
 INLINE LODNode::
 LODNode(const string &name) :
-  SelectiveChildNode(name)
+  PandaNode(name)
 {
 }
 
@@ -54,7 +156,7 @@ LODNode(const string &name) :
 ////////////////////////////////////////////////////////////////////
 INLINE LODNode::
 LODNode(const LODNode &copy) :
-  SelectiveChildNode(copy),
+  PandaNode(copy),
   _cycler(copy._cycler)
 {
 }
@@ -75,7 +177,8 @@ LODNode(const LODNode &copy) :
 INLINE void LODNode::
 add_switch(float in, float out) {
   CDWriter cdata(_cycler);
-  cdata->_lod._switch_vector.push_back(LODSwitch(in, out));
+  cdata->_switch_vector.push_back(Switch(in, out));
+  cdata->check_limits();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -87,8 +190,9 @@ add_switch(float in, float out) {
 INLINE bool LODNode::
 set_switch(int index, float in, float out) {
   CDWriter cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), false);
-  cdata->_lod._switch_vector[index].set_range(in, out);
+  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), false);
+  cdata->_switch_vector[index].set_range(in, out);
+  cdata->check_limits();
   return true;
 }
 
@@ -102,8 +206,9 @@ set_switch(int index, float in, float out) {
 INLINE void LODNode::
 clear_switches(void) {
   CDWriter cdata(_cycler);
-  cdata->_lod._switch_vector.erase(cdata->_lod._switch_vector.begin(),
-                                   cdata->_lod._switch_vector.end());
+  cdata->_switch_vector.clear();
+  cdata->_lowest = 0;
+  cdata->_highest = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -117,7 +222,7 @@ clear_switches(void) {
 INLINE int LODNode::
 get_num_switches() const {
   CDReader cdata(_cycler);
-  return cdata->_lod._switch_vector.size();
+  return cdata->_switch_vector.size();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -130,8 +235,8 @@ get_num_switches() const {
 INLINE float LODNode::
 get_in(int index) const {
   CDReader cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
-  return cdata->_lod._switch_vector[index].get_in();
+  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), 0.0);
+  return cdata->_switch_vector[index].get_in();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -144,8 +249,63 @@ get_in(int index) const {
 INLINE float LODNode::
 get_out(int index) const {
   CDReader cdata(_cycler);
-  nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
-  return cdata->_lod._switch_vector[index].get_out();
+  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), 0.0);
+  return cdata->_switch_vector[index].get_out();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::get_lowest_switch
+//       Access: Published
+//  Description: Returns the index number of the child with the lowest
+//               level of detail; that is, the one that is designed to
+//               be seen from the farthest away.  This is usually the
+//               first child, but it is not necessarily so.
+////////////////////////////////////////////////////////////////////
+INLINE int LODNode::
+get_lowest_switch() const {
+  CDReader cdata(_cycler);
+  return cdata->_lowest;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::get_highest_switch
+//       Access: Published
+//  Description: Returns the index number of the child with the highest
+//               level of detail; that is, the one that is designed to
+//               be seen from the closest to the camera.  This is
+//               usually the last child, but it is not necessarily so.
+////////////////////////////////////////////////////////////////////
+INLINE int LODNode::
+get_highest_switch() const {
+  CDReader cdata(_cycler);
+  return cdata->_highest;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::force_switch
+//       Access: Published
+//  Description: Forces the LODNode to show the indicated level
+//               instead of the level that would normally be shown
+//               based on the distance from the camera.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::
+force_switch(int index) {
+  CDWriter cdata(_cycler);
+  cdata->_force_switch = index;
+  cdata->_got_force_switch = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::clear_force_switch
+//       Access: Published
+//  Description: Undoes the effect of a previous call to
+//               force_switch() and releases the LODNode to once again
+//               display the normal level.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::
+clear_force_switch() {
+  CDWriter cdata(_cycler);
+  cdata->_got_force_switch = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -158,7 +318,7 @@ get_out(int index) const {
 INLINE void LODNode::
 set_center(const LPoint3f &center) {
   CDWriter cdata(_cycler);
-  cdata->_lod._center = center;
+  cdata->_center = center;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -171,5 +331,5 @@ set_center(const LPoint3f &center) {
 INLINE const LPoint3f &LODNode::
 get_center() const {
   CDReader cdata(_cycler);
-  return cdata->_lod._center;
+  return cdata->_center;
 }

+ 153 - 25
panda/src/pgraph/lodNode.cxx

@@ -32,6 +32,26 @@ make_copy() const {
   return new CData(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::check_limits
+//       Access: Public
+//  Description: Ensures that the _lowest and _highest members are set
+//               appropriately after a change to the set of switches.
+////////////////////////////////////////////////////////////////////
+void LODNode::CData::
+check_limits() {
+  _lowest = 0;
+  _highest = 0;
+  for (size_t i = 1; i < _switch_vector.size(); ++i) {
+    if (_switch_vector[i].get_out() > _switch_vector[_lowest].get_out()) {
+      _lowest = i;
+    }
+    if (_switch_vector[i].get_in() < _switch_vector[_highest].get_in()) {
+      _highest = i;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::CData::write_datagram
 //       Access: Public, Virtual
@@ -40,7 +60,16 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void LODNode::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
-  _lod.write_datagram(dg);
+  _center.write_datagram(dg);
+
+  dg.add_uint16(_switch_vector.size());
+
+  SwitchVector::const_iterator si;
+  for (si = _switch_vector.begin();
+       si != _switch_vector.end();
+       ++si) {
+    (*si).write_datagram(dg);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -52,7 +81,24 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 ////////////////////////////////////////////////////////////////////
 void LODNode::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  _lod.read_datagram(scan);
+  _center.read_datagram(scan);
+
+  _switch_vector.clear();
+
+  int num_switches = scan.get_uint16();
+  _switch_vector.reserve(num_switches);
+  for (int i = 0; i < num_switches; i++) {
+    Switch sw(0, 0);
+    sw.read_datagram(scan);
+
+    if (manager->get_file_minor_ver() < 13) {
+      // Before bam version 4.13, we stored the square of the
+      // switching distance in the bam files.
+      sw.set_range(sqrtf(sw.get_in()), sqrtf(sw.get_out()));
+    }
+
+    _switch_vector.push_back(sw);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -93,7 +139,20 @@ safe_to_combine() const {
 void LODNode::
 xform(const LMatrix4f &mat) {
   CDWriter cdata(_cycler);
-  cdata->_lod.xform(mat);
+
+  cdata->_center = cdata->_center * mat;
+
+  // We'll take just the length of the y axis as the matrix's scale.
+  LVector3f y;
+  mat.get_row3(y, 1);
+  float factor = y.length();
+
+  SwitchVector::iterator si;
+  for (si = cdata->_switch_vector.begin(); 
+       si != cdata->_switch_vector.end(); 
+       ++si) {
+    (*si).rescale(factor);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -131,26 +190,15 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 bool LODNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
-  if (data._net_transform->is_singular()) {
-    // If we're under a singular transform, we can't compute the LOD;
-    // select none of them instead.
-    select_child(get_num_children());
-
-  } else { 
-    CDReader cdata(_cycler);
-    LPoint3f camera_pos(0, 0, 0);
-
-    // Get the LOD center in camera space
-    CPT(TransformState) rel_transform =
-      trav->get_camera_transform()->invert_compose(data._net_transform);
-    LPoint3f center = cdata->_lod._center * rel_transform->get_mat();
-    
-    // Determine which child to traverse
-    int index = cdata->_lod.compute_child(camera_pos, center);
-    select_child(index);
+  int index = compute_child(trav, data);
+  if (index >= 0 && index < get_num_children()) {
+    CullTraverserData next_data(data, get_child(index));
+    trav->traverse(next_data);
   }
 
-  return true;
+  // Now return false indicating that we have already taken care of
+  // the traversal from here.
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -160,10 +208,90 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 void LODNode::
 output(ostream &out) const {
-  SelectiveChildNode::output(out);
+  PandaNode::output(out);
   CDReader cdata(_cycler);
   out << " ";
-  cdata->_lod.output(out);
+  if (cdata->_switch_vector.empty()) {
+    out << "no switches.";
+  } else {
+    SwitchVector::const_iterator si;
+    si = cdata->_switch_vector.begin();
+    out << "(" << (*si).get_in() << "/" << (*si).get_out() << ")";
+    ++si;
+    while (si != cdata->_switch_vector.end()) {
+      out << " (" << (*si).get_in() << "/" << (*si).get_out() << ")";
+      ++si;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::is_lod_node
+//       Access: Published, Virtual
+//  Description: A simple downcast check.  Returns true if this kind
+//               of node happens to inherit from LODNode, false
+//               otherwise.
+//
+//               This is provided as a a faster alternative to calling
+//               is_of_type(LODNode::get_class_type()).
+////////////////////////////////////////////////////////////////////
+bool LODNode::
+is_lod_node() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::compute_child
+//       Access: Protected
+//  Description: Determines which child should be visible according to
+//               the current camera position.  If a child is visible,
+//               returns its index number; otherwise, returns -1.
+////////////////////////////////////////////////////////////////////
+int LODNode::
+compute_child(CullTraverser *trav, CullTraverserData &data) {
+  if (data._net_transform->is_singular()) {
+    // If we're under a singular transform, we can't compute the LOD;
+    // select none of them instead.
+    return -1;
+  }
+   
+  CDReader cdata(_cycler);
+
+  if (cdata->_got_force_switch) {
+    return cdata->_force_switch;
+  }
+  
+  // Get the LOD center in camera space
+  //  CPT(TransformState) rel_transform =
+  //    trav->get_camera_transform()->invert_compose(data._net_transform);
+
+  CPT(TransformState) rel_transform = 
+    trav->get_scene()->get_cull_center().get_net_transform()->
+    invert_compose(data._net_transform);
+
+  LPoint3f center = cdata->_center * rel_transform->get_mat();
+
+  // Determine which child to traverse
+  float dist = dot(center, LVector3f::forward());
+
+  for (int index = 0; index < (int)cdata->_switch_vector.size(); index++) {
+    if (cdata->_switch_vector[index].in_range(dist)) { 
+      if (pgraph_cat.is_debug()) {
+        pgraph_cat.debug()
+          << data._node_path << " at distance " << dist << ", selected child "
+          << index << "\n";
+      }
+
+      return index;
+    }
+  }
+
+  if (pgraph_cat.is_debug()) {
+    pgraph_cat.debug()
+      << data._node_path << " at distance " << dist << ", no children in range.\n";
+  }
+
+  return -1;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -185,7 +313,7 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void LODNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  SelectiveChildNode::write_datagram(manager, dg);
+  PandaNode::write_datagram(manager, dg);
   manager->write_cdata(dg, _cycler);
 }
 
@@ -218,6 +346,6 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void LODNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  SelectiveChildNode::fillin(scan, manager);
+  PandaNode::fillin(scan, manager);
   manager->read_cdata(scan, _cycler);
 }

+ 50 - 13
panda/src/pgraph/lodNode.h

@@ -21,9 +21,7 @@
 
 #include "pandabase.h"
 
-#include "selectiveChildNode.h"
-
-#include "LOD.h"
+#include "pandaNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : LODNode
@@ -32,7 +30,7 @@
 //               from the camera and the table indicated in the
 //               associated LOD object.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA LODNode : public SelectiveChildNode {
+class EXPCL_PANDA LODNode : public PandaNode {
 PUBLISHED:
   INLINE LODNode(const string &name);
 
@@ -47,6 +45,8 @@ public:
 
   virtual void output(ostream &out) const;
 
+  virtual bool is_lod_node() const;
+
 PUBLISHED:
   // The sense of in vs. out distances is as if the object were coming
   // towards you from far away: it switches "in" at the far distance,
@@ -61,41 +61,78 @@ PUBLISHED:
   INLINE float get_in(int index) const;
   INLINE float get_out(int index) const;
 
+  INLINE int get_lowest_switch() const;
+  INLINE int get_highest_switch() const;
+
+  INLINE void force_switch(int index);
+  INLINE void clear_force_switch();
+
   INLINE void set_center(const LPoint3f &center);
   INLINE const LPoint3f &get_center() const;
 
-public:
-  static void register_with_read_factory();
-  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+protected:
+  int compute_child(CullTraverser *trav, CullTraverserData &data);
 
 protected:
-  static TypedWritable *make_from_bam(const FactoryParams &params);
-  void fillin(DatagramIterator &scan, BamReader *manager);
+  class Switch {
+  public:
+    INLINE Switch(float in, float out);
+    INLINE float get_in() const;
+    INLINE float get_out() const;
+
+    INLINE void set_range(float in, float out);
+    INLINE bool in_range(float dist) const;
+    
+    INLINE void rescale(float factor);
+
+    INLINE void write_datagram(Datagram &destination) const;
+    INLINE void read_datagram(DatagramIterator &source);
+
+  private:
+    float _in;
+    float _out;
+  };
+  typedef pvector<Switch> SwitchVector;
 
-private:
   class EXPCL_PANDA CData : public CycleData {
   public:
     INLINE CData();
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
+
+    void check_limits();
+
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
-    LOD _lod;
+    LPoint3f _center;
+    SwitchVector _switch_vector;
+    size_t _lowest, _highest;
+
+    bool _got_force_switch;
+    int _force_switch;
   };
 
   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() {
-    SelectiveChildNode::init_type();
+    PandaNode::init_type();
     register_type(_type_handle, "LODNode",
-                  SelectiveChildNode::get_class_type());
+                  PandaNode::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

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

@@ -1348,6 +1348,21 @@ is_geom_node() const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::is_lod_node
+//       Access: Published, Virtual
+//  Description: A simple downcast check.  Returns true if this kind
+//               of node happens to inherit from LODNode, false
+//               otherwise.
+//
+//               This is provided as a a faster alternative to calling
+//               is_of_type(LODNode::get_class_type()).
+////////////////////////////////////////////////////////////////////
+bool PandaNode::
+is_lod_node() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::as_light
 //       Access: Published, Virtual

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

@@ -193,6 +193,7 @@ PUBLISHED:
   INLINE const BoundingVolume &get_internal_bound() const;
 
   virtual bool is_geom_node() const;
+  virtual bool is_lod_node() const;
   virtual Light *as_light();
 
 protected:

+ 2 - 1
panda/src/putil/bam.h

@@ -34,7 +34,7 @@ static const unsigned short _bam_major_ver = 4;
 // Bumped to major version 3 on 12/8/00 to change float64's to float32's.
 // Bumped to major version 4 on 4/10/02 to store new scene graph.
 
-static const unsigned short _bam_minor_ver = 12;
+static const unsigned short _bam_minor_ver = 13;
 // Bumped to minor version 1 on 4/10/03 to add CullFaceAttrib::reverse.
 // Bumped to minor version 1 on 4/10/03 to add CullFaceAttrib::reverse.
 // Bumped to minor version 2 on 4/12/03 to add num_components to texture.
@@ -48,6 +48,7 @@ static const unsigned short _bam_minor_ver = 12;
 // Bumped to minor version 10 on 04/23/04 to make ComputedVertices use uint32's.
 // Bumped to minor version 11 on 07/26/04 to add multitexture pointers.
 // Bumped to minor version 12 on 09/22/04 to add PandaNode::into_collide_mask.
+// Bumped to minor version 13 on 09/24/04 to store actual LODNode switch distances instead of squares.
 
 
 #endif

+ 4 - 5
panda/src/testbed/Sources.pp

@@ -33,12 +33,11 @@
 #end test_bin_target
 
 #begin test_bin_target
-  #define TARGET test_texmem
-  #define SOURCES test_texmem.cxx
+  #define TARGET test_lod
+  #define SOURCES test_lod.cxx
 #end test_bin_target
 
 #begin test_bin_target
-  #define TARGET test_particles
-  
-  #define SOURCES test_particles.cxx
+  #define TARGET test_texmem
+  #define SOURCES test_texmem.cxx
 #end test_bin_target

+ 0 - 54
panda/src/testbed/lod_test.cxx

@@ -1,54 +0,0 @@
-// Filename: lod_test.cxx
-// Created by:  
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "pandabase.h"
-#include "namedNode.h"
-
-#include "eventHandler.h"
-#include "chancfg.h"
-#include "textNode.h"
-#include "eggLoader.h"
-#include "LOD.h"
-#include "LODNode.h"
-#include "pt_NamedNode.h"
-
-extern PT_NamedNode render;
-extern PT_NamedNode egg_root;
-extern EventHandler event_handler;
-
-extern int framework_main(int argc, char *argv[]);
-extern void (*define_keys)(EventHandler&);
-
-void lod_keys(EventHandler& eh) {
-
-  PT(LODNode) lodnode = new LODNode("lodnode");
-  new RenderRelation(egg_root, lodnode);
-
-  PT_NamedNode lodnode0 = loader.load_sync("smiley.egg");
-  new RenderRelation(lodnode, lodnode0);
-  lodnode->add_switch(10, 0);
-
-  PT_NamedNode lodnode1 = loader.load_sync("frowney.egg");
-  new RenderRelation(lodnode, lodnode1);
-  lodnode->add_switch(1000, 10);
-}
-
-int main(int argc, char *argv[]) {
-  define_keys = &lod_keys;
-  return framework_main(argc, argv);
-}

+ 74 - 0
panda/src/testbed/test_lod.cxx

@@ -0,0 +1,74 @@
+// Filename: test_lod.cxx
+// Created by:  drose (24Sep04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandaFramework.h"
+#include "lodNode.h"
+#include "fadeLodNode.h"
+#include "loader.h"
+
+PandaFramework framework;
+
+char *filename_a = "smiley";
+char *filename_b = "frowney";
+
+void
+create_lod_node(NodePath &models, const char *a, const char *b) {
+  Loader loader;
+  PT(PandaNode) smiley = loader.load_sync(a);
+  PT(PandaNode) frowney = loader.load_sync(b);
+
+  PT(LODNode) lod = new FadeLODNode("lod");
+  //PT(LODNode) lod = new LODNode("lod");
+  if (!smiley.is_null()) {
+    lod->add_child(smiley);
+    lod->add_switch(10, 0);
+  }
+  if (!frowney.is_null()) {
+    lod->add_child(frowney);
+    lod->add_switch(1000, 10);
+  }
+
+  models.attach_new_node(lod);
+}
+
+
+int
+main(int argc, char *argv[]) {
+  framework.open_framework(argc, argv);
+  framework.set_window_title("LOD Test");
+
+  WindowFramework *window = framework.open_window();
+  if (window != (WindowFramework *)NULL) {
+    // We've successfully opened a window.
+
+    window->enable_keyboard();
+    window->setup_trackball();
+    framework.get_models().instance_to(window->get_render());
+
+    create_lod_node(framework.get_models(),
+                    argc > 1 ? argv[1] : filename_a,
+                    argc > 2 ? argv[2] : filename_b);
+
+    window->center_trackball(framework.get_models());
+
+    framework.enable_default_keys();
+    framework.main_loop();
+  }
+
+  return (0);
+}

+ 0 - 649
panda/src/testbed/test_particles.cxx

@@ -1,649 +0,0 @@
-// Filename: test_particles.cxx
-// Created by:  
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "framework.h"
-
-#include "eventHandler.h"
-#include "chancfg.h"
-#include <string>
-#include "renderModeTransition.h"
-#include "colorTransition.h"
-#include "cullFaceTransition.h"
-#include "depthTestTransition.h"
-#include "depthWriteTransition.h"
-#include "textureTransition.h"
-#include "lightTransition.h"
-#include "materialTransition.h"
-#include "transformTransition.h"
-#include "get_rel_pos.h"
-#include "boundingSphere.h"
-#include "geomSphere.h"
-#include "geomNode.h"
-#include "notify.h"
-#include "directionalLight.h"
-#include "renderRelation.h"
-#include "camera.h"
-#include "frustum.h"
-#include "orthoProjection.h"
-#include "perspectiveProjection.h"
-#include "textNode.h"
-#include "physicsManager.h"
-#include "particleSystem.h"
-#include "emitters.h"
-#include "particlefactories.h"
-#include "forces.h"
-#include "sparkleParticleRenderer.h"
-#include "pointParticleRenderer.h"
-#include "lineParticleRenderer.h"
-#include "geomParticleRenderer.h"
-#include "spriteParticleRenderer.h"
-#include "colorTransition.h"
-#include "particleSystemManager.h"
-#include "clockObject.h"
-#include "nodePath.h"
-#include "pointShapeTransition.h"
-#include "texture.h"
-#include "texturePool.h"
-#include "physicalNode.h"
-#include "forceNode.h"
-#include "linearEulerIntegrator.h"
-
-// physics.  particle systems.
-
-/////////////////////////////////////////////////
-
-// particle system manager params
-#define PARTICLE_SYSTEM_MANAGER_FRAME_STEPPING 1
-
-// particle system params
-#define PARTICLE_SYSTEM_POOL_SIZE 1000
-#define PARTICLE_SYSTEM_BIRTH_RATE  0.01f
-//#define PARTICLE_SYSTEM_BIRTH_RATE  0.5f
-#define PARTICLE_SYSTEM_LITTER_SIZE 10
-#define PARTICLE_SYSTEM_LITTER_SPREAD 0
-
-
-//#define WIND_FORCE  0.5, 0, 0
-//#define NOISE_FORCE 0.02f
-
-/////////////////////////////////////////////////
-
-// particle factory selection
-#define POINT_PARTICLES
-//#define ZSPIN_PARTICLES
-//#define ORIENTED_PARTICLES
-
-// particle renderer selection
-//#define POINT_PARTICLE_RENDERER
-#define LINE_PARTICLE_RENDERER
-//#define SPARKLE_PARTICLE_RENDERER
-//#define SPRITE_PARTICLE_RENDERER
-//#define GEOM_PARTICLE_RENDERER
-
-// particle emitter selection
-//#define BOX_EMITTER
-//#define DISC_EMITTER
-//#define LINE_EMITTER
-//#define POINT_EMITTER
-//#define RECTANGLE_EMITTER
-//#define RING_EMITTER
-//#define SPHERE_SURFACE_EMITTER
-#define SPHERE_VOLUME_EMITTER
-//#define TANGENT_RING_EMITTER
-
-/////////////////////////////////////////////////
-
-// particle factory params
-#define PARTICLE_FACTORY_LIFESPAN_BASE   5.0f
-//#define PARTICLE_FACTORY_LIFESPAN_BASE   3.0f
-//#define PARTICLE_FACTORY_LIFESPAN_SPREAD 1.0f
-//#define PARTICLE_FACTORY_MASS_BASE       1.0f
-//#define PARTICLE_FACTORY_MASS_SPREAD     0.2f
-//#define PARTICLE_FACTORY_TERMINAL_VELOCITY_BASE   PhysicsObject::_default_terminal_velocity
-//#define PARTICLE_FACTORY_TERMINAL_VELOCITY_SPREAD 0.0f
-
-#ifdef ZSPIN_PARTICLES
-  #define ZSPIN_PARTICLES_INITIAL_ANGLE         0.0f
-  #define ZSPIN_PARTICLES_FINAL_ANGLE           0.0f
-  #define ZSPIN_PARTICLES_INITIAL_ANGLE_SPREAD  90.0f
-  #define ZSPIN_PARTICLES_FINAL_ANGLE_SPREAD    90.0f
-#endif
-
-// particle renderer params
-#define SPRITE_PARTICLE_RENDERER_ALPHA_DISABLE      false
-#define PARTICLE_RENDERER_ALPHA_MODE  BaseParticleRenderer::PR_ALPHA_NONE
-//#define PARTICLE_RENDERER_ALPHA_MODE  BaseParticleRenderer::PR_ALPHA_IN
-//#define PARTICLE_RENDERER_ALPHA_MODE  BaseParticleRenderer::PR_ALPHA_OUT
-//#define PARTICLE_RENDERER_ALPHA_MODE  BaseParticleRenderer::PR_ALPHA_USER
-//#define PARTICLE_RENDERER_USER_ALPHA  1.0
-
-#ifdef GEOM_PARTICLE_RENDERER
-#elif defined POINT_PARTICLE_RENDERER
-  #define POINT_PARTICLE_RENDERER_POINT_SIZE    1.0f
-//  #define POINT_PARTICLE_RENDERER_STARTCOLOR    Colorf(1.0f, 0.0f, 0.0f, 1.0f)
-//  #define POINT_PARTICLE_RENDERER_ENDCOLOR      Colorf(1.0f, 1.0f, 0.0f, 1.0f)
-//  #define POINT_PARTICLE_RENDERER_BLEND_TYPE    BaseParticleRenderer::PP_BLEND_LIFE
-//  #define POINT_PARTICLE_RENDERER_BLEND_METHOD  BaseParticleRenderer::PP_BLEND_LINEAR
-//  #define POINT_PARTICLE_RENDERER_BLEND_METHOD  BaseParticleRenderer::PP_BLEND_CUBIC
-
-#elif defined LINE_PARTICLE_RENDERER
-  #define LINE_PARTICLE_RENDERER_HEAD_COLOR   Colorf(1.0f, 1.0f, 1.0f, 1.0f)
-  #define LINE_PARTICLE_RENDERER_TAIL_COLOR   Colorf(1.0f, 1.0f, 1.0f, 1.0f)
-#elif defined SPARKLE_PARTICLE_RENDERER
-//  #define SPARKLE_PARTICLE_RENDERER_CENTER_COLOR  Colorf(1.0f, 0.0f, 0.0f, 1.0f)
-//  #define SPARKLE_PARTICLE_RENDERER_EDGE_COLOR    Colorf(0.0f, 0.0f, 1.0f, 1.0f)
-//  #define SPARKLE_PARTICLE_RENDERER_BIRTH_RADIUS  0.004f
-//  #define SPARKLE_PARTICLE_RENDERER_DEATH_RADIUS  1.0f
-//  #define SPARKLE_PARTICLE_RENDERER_LIFE_SCALE    SP_SCALE
-#elif defined SPRITE_PARTICLE_RENDERER
-//  #define SPRITE_PARTICLE_RENDERER_TEXTURE_FILE       "smoke.rgba"
-//  #define SPRITE_PARTICLE_RENDERER_TEXTURE_FILE       "lilsmiley.rgba"
-  #define SPRITE_PARTICLE_RENDERER_TEXTURE_FILE       "rock-floor.rgb"
-  #define SPRITE_PARTICLE_RENDERER_COLOR              Colorf(1.0f, 1.0f, 1.0f, 1.0f)
-//  #define SPRITE_PARTICLE_RENDERER_X_SCALE_FLAG       true
-//  #define SPRITE_PARTICLE_RENDERER_Y_SCALE_FLAG       true
-  #define SPRITE_PARTICLE_RENDERER_ANIM_ANGLE_FLAG    true
-//  #define SPRITE_PARTICLE_RENDERER_INITIAL_X_SCALE    0.1
-//  #define SPRITE_PARTICLE_RENDERER_FINAL_X_SCALE      0.0
-//  #define SPRITE_PARTICLE_RENDERER_INITIAL_Y_SCALE    0.0
-//  #define SPRITE_PARTICLE_RENDERER_FINAL_Y_SCALE      0.5
-  #define SPRITE_PARTICLE_RENDERER_NONANIMATED_THETA  45.0f
-  #define SPRITE_PARTICLE_RENDERER_BLEND_METHOD       BaseParticleRenderer::PP_BLEND_LINEAR
-//  #define SPRITE_PARTICLE_RENDERER_BLEND_METHOD       BaseParticleRenderer::PP_BLEND_CUBIC
-//  #define SPRITE_PARTICLE_RENDERER_ALPHA_DISABLE      false
-#endif
-
-// particle emitter params
-
-#define EMISSION_TYPE_EXPLICIT
-//#define EMISSION_TYPE_RADIATE
-//#define EMISSION_TYPE_CUSTOM
-
-//#define EMITTER_AMPLITUDE 10.0
-#define EMITTER_AMPLITUDE 1.0
-//#define EMITTER_AMPLITUDE 0.5
-//#define EMITTER_AMPLITUDE_SPREAD 2.0
-//#define EMITTER_OFFSET_FORCE LVector3f(1.0, 1.0, 1.0)
-
-#if defined EMISSION_TYPE_EXPLICIT
-  #define EXPLICIT_LAUNCH_VEC LVector3f(-1.0f, -1.0f, 1.0f)
-//  #define EXPLICIT_LAUNCH_VEC LVector3f(0.0f, 0.0f, 0.0f)
-#elif defined EMISSION_TYPE_RADIATE
-//  #define RADIATE_ORIGIN LPoint3f(0.0f, 0.0f, 0.5f)
-  #define RADIATE_ORIGIN LPoint3f(0.0f, 0.0f, 0.0f)
-#endif
-
-#ifdef BOX_EMITTER
-  #define BOX_EMITTER_MINBOUND LPoint3f(-1.0f, -1.0f, -1.0f)
-  #define BOX_EMITTER_MAXBOUND LPoint3f(1.0f, 1.0f, 1.0f)
-#elif defined DISC_EMITTER
-  #define DISC_EMITTER_RADIUS           1.0f
-  #ifdef EMISSION_TYPE_CUSTOM
-    #define DISC_EMITTER_OUTER_ANGLE      45.0f
-    #define DISC_EMITTER_INNER_ANGLE      90.0f
-    #define DISC_EMITTER_OUTER_MAGNITUDE  1.5f
-    #define DISC_EMITTER_INNER_MAGNITUDE  1.5f
-    #define DISC_EMITTER_CUBIC_LERPING    false
-  #endif
-#elif defined LINE_EMITTER
-  #define LINE_EMITTER_ENDPOINT1 LPoint3f(-2.0f, 0.0f, 2.0f)
-  #define LINE_EMITTER_ENDPOINT2 LPoint3f(2.0f, 0.0f, 2.0f)
-#elif defined POINT_EMITTER
-  #define POINT_EMITTER_LOCATION LPoint3f(1.0f, 1.0f, 1.0f)
-#elif defined RECTANGLE_EMITTER
-  #define RECTANGLE_EMITTER_MINBOUND LPoint2f(-1.0f, -1.0f)
-  #define RECTANGLE_EMITTER_MAXBOUND LPoint2f(1.0f, 1.0f)
-#elif defined RING_EMITTER
-  #define RING_EMITTER_RADIUS     0.5f
-  #ifdef EMISSION_TYPE_CUSTOM
-    #define RING_EMITTER_ANGLE      90.0f
-  #endif
-#elif defined SPHERE_SURFACE_EMITTER
-  #define SPHERE_SURFACE_EMITTER_RADIUS 1.0f
-#elif defined SPHERE_VOLUME_EMITTER
-  #define SPHERE_VOLUME_EMITTER_RADIUS 1.0f
-#elif defined TANGENT_RING_EMITTER
-  #define TANGENT_RING_EMITTER_RADIUS 1.0f
-#endif
-
-/////////////////////////////////////////////////
-
-
-PhysicsManager physics_manager;
-ParticleSystemManager ps_manager;
-
-PT(ParticleSystem) particle_system = new ParticleSystem(PARTICLE_SYSTEM_POOL_SIZE);
-
-static int particles_added = 0;
-
-/*
-#if defined POINT_PARTICLES
-  PT(PointParticleFactory) pf = new PointParticleFactory;
-#elif defined ZSPIN_PARTICLES
-  PT(ZSpinParticleFactory) pf = new ZSpinParticleFactory;
-#elif defined ORIENTED_PARTICLES
-  PT(OrientedParticleFactory) pf = new OrientedParticleFactory;
-#endif
-*/
-#if defined POINT_PARTICLES
-  PT(BaseParticleFactory) pf = new PointParticleFactory;
-#elif defined ZSPIN_PARTICLES
-  PT(BaseParticleFactory) pf = new ZSpinParticleFactory;
-#elif defined ORIENTED_PARTICLES
-  PT(BaseParticleFactory) pf = new OrientedParticleFactory;
-#endif
-
-#if defined GEOM_PARTICLE_RENDERER
-  PT(GeomParticleRenderer) pr = new GeomParticleRenderer;
-#elif defined POINT_PARTICLE_RENDERER
-  PT(PointParticleRenderer) pr = new PointParticleRenderer;
-#elif defined LINE_PARTICLE_RENDERER
-  PT(LineParticleRenderer) pr = new LineParticleRenderer;
-#elif defined SPARKLE_PARTICLE_RENDERER
-  PT(SparkleParticleRenderer) pr = new SparkleParticleRenderer;
-#elif defined SPRITE_PARTICLE_RENDERER
-  PT(SpriteParticleRenderer) pr = new SpriteParticleRenderer;
-#endif
-
-#if defined BOX_EMITTER
-  PT(BoxEmitter) pe = new BoxEmitter;
-#elif defined DISC_EMITTER
-  PT(DiscEmitter) pe = new DiscEmitter;
-#elif defined LINE_EMITTER
-  PT(LineEmitter) pe = new LineEmitter;
-#elif defined POINT_EMITTER
-  PT(PointEmitter) pe = new PointEmitter;
-#elif defined RECTANGLE_EMITTER
-  PT(RectangleEmitter) pe = new RectangleEmitter;
-#elif defined RING_EMITTER
-  PT(RingEmitter) pe = new RingEmitter;
-#elif defined SPHERE_SURFACE_EMITTER
-  PT(SphereSurfaceEmitter) pe = new SphereSurfaceEmitter;
-#elif defined TANGENT_RING_EMITTER
-  PT(TangentRingEmitter) pe = new TangentRingEmitter;
-#elif defined SPHERE_VOLUME_EMITTER
-  PT(SphereVolumeEmitter) pe = new SphereVolumeEmitter;
-#endif
-
-PT(Texture) texture;
-
-#ifdef WIND_FORCE
-PT(LinearVectorForce) wind_force = new LinearVectorForce(WIND_FORCE);
-#endif
-#ifdef NOISE_FORCE
-PT(LinearNoiseForce) noise_force = new LinearNoiseForce(NOISE_FORCE);
-#endif
-
-PT(PhysicalNode) pn = new PhysicalNode;
-PT(ForceNode) fn = new ForceNode;
-
-static void
-event_csn_update(CPT_Event) {
-  float dt = ClockObject::get_global_clock()->get_dt();
-
-  physics_manager.do_physics(dt);
-  ps_manager.do_particles(dt);
-}
-
-static void
-event_add_particles(CPT_Event) {
-
-  // guard against additional "P" presses (bad things happen)
-  if(particles_added) return;
-  particles_added = 1;
-
-  // renderer setup
-  #ifdef PARTICLE_RENDERER_ALPHA_MODE
-    pr->set_alpha_mode(PARTICLE_RENDERER_ALPHA_MODE);
-  #endif
-  #ifdef PARTICLE_RENDERER_USER_ALPHA
-    pr->set_user_alpha(PARTICLE_RENDERER_USER_ALPHA);
-  #endif
-
-  #ifdef GEOM_PARTICLE_RENDERER
-  #elif defined POINT_PARTICLE_RENDERER
-    #ifdef POINT_PARTICLE_RENDERER_POINT_SIZE
-      pr->set_point_size(POINT_PARTICLE_RENDERER_POINT_SIZE);
-    #endif
-    #ifdef POINT_PARTICLE_RENDERER_STARTCOLOR
-      pr->set_color1(POINT_PARTICLE_RENDERER_STARTCOLOR);
-    #endif
-    #ifdef POINT_PARTICLE_RENDERER_ENDCOLOR
-      pr->set_color2(POINT_PARTICLE_RENDERER_ENDCOLOR);
-    #endif
-    #ifdef POINT_PARTICLE_RENDERER_BLEND_TYPE
-      pr->set_blend_type(POINT_PARTICLE_RENDERER_BLEND_TYPE);
-    #endif
-    #ifdef POINT_PARTICLE_RENDERER_BLEND_METHOD
-      pr->set_blend_method(POINT_PARTICLE_RENDERER_BLEND_METHOD);
-    #endif
-  #elif defined LINE_PARTICLE_RENDERER
-    #ifdef LINE_PARTICLE_RENDERER_HEAD_COLOR
-      pr->set_head_color(LINE_PARTICLE_RENDERER_HEAD_COLOR);
-    #endif
-    #ifdef LINE_PARTICLE_RENDERER_TAIL_COLOR
-      pr->set_tail_color(LINE_PARTICLE_RENDERER_TAIL_COLOR);
-    #endif
-  #elif defined SPARKLE_PARTICLE_RENDERER
-    #ifdef SPARKLE_PARTICLE_RENDERER_CENTER_COLOR
-      pr->set_center_color(SPARKLE_PARTICLE_RENDERER_CENTER_COLOR);
-    #endif
-    #ifdef SPARKLE_PARTICLE_RENDERER_EDGE_COLOR
-      pr->set_edge_color(SPARKLE_PARTICLE_RENDERER_EDGE_COLOR);
-    #endif
-    #ifdef SPARKLE_PARTICLE_RENDERER_LIFE_SCALE
-      pr->set_life_scale(SPARKLE_PARTICLE_RENDERER_LIFE_SCALE);
-    #endif
-    #ifdef SPARKLE_PARTICLE_RENDERER_BIRTH_RADIUS
-      pr->set_birth_mag(SPARKLE_PARTICLE_RENDERER_BIRTH_RADIUS);
-    #endif
-    #ifdef SPARKLE_PARTICLE_RENDERER_DEATH_RADIUS
-      pr->set_death_mag(SPARKLE_PARTICLE_RENDERER_DEATH_RADIUS);
-    #endif
-  #elif defined SPRITE_PARTICLE_RENDERER
-    pr->set_texture(texture);
-    #ifdef SPRITE_PARTICLE_RENDERER_COLOR
-      pr->set_color(SPRITE_PARTICLE_RENDERER_COLOR);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_X_SCALE_FLAG
-      pr->set_x_scale_flag(SPRITE_PARTICLE_RENDERER_X_SCALE_FLAG);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_Y_SCALE_FLAG
-      pr->set_y_scale_flag(SPRITE_PARTICLE_RENDERER_Y_SCALE_FLAG);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_ANIM_ANGLE_FLAG
-      pr->set_anim_angle_flag(SPRITE_PARTICLE_RENDERER_ANIM_ANGLE_FLAG);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_INITIAL_X_SCALE
-      pr->set_initial_x_scale(SPRITE_PARTICLE_RENDERER_INITIAL_X_SCALE);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_FINAL_X_SCALE
-      pr->set_final_x_scale(SPRITE_PARTICLE_RENDERER_FINAL_X_SCALE);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_INITIAL_Y_SCALE
-      pr->set_initial_y_scale(SPRITE_PARTICLE_RENDERER_INITIAL_Y_SCALE);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_FINAL_Y_SCALE
-      pr->set_final_y_scale(SPRITE_PARTICLE_RENDERER_FINAL_Y_SCALE);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_NONANIMATED_THETA
-      pr->set_nonanimated_theta(SPRITE_PARTICLE_RENDERER_NONANIMATED_THETA);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_BLEND_METHOD
-      pr->set_alpha_blend_method(SPRITE_PARTICLE_RENDERER_BLEND_METHOD);
-    #endif
-    #ifdef SPRITE_PARTICLE_RENDERER_ALPHA_DISABLE
-      pr->set_alpha_disable(SPRITE_PARTICLE_RENDERER_ALPHA_DISABLE);
-    #endif
-  #endif
-
-  // factory setup
-
-  #ifdef PARTICLE_FACTORY_LIFESPAN_BASE
-    pf->set_lifespan_base(PARTICLE_FACTORY_LIFESPAN_BASE);
-  #endif
-  #ifdef PARTICLE_FACTORY_LIFESPAN_SPREAD
-    pf->set_lifespan_spread(PARTICLE_FACTORY_LIFESPAN_SPREAD);
-  #endif
-  #ifdef PARTICLE_FACTORY_MASS_BASE
-    pf->set_mass_base(PARTICLE_FACTORY_MASS_BASE);
-  #endif
-  #ifdef PARTICLE_FACTORY_MASS_SPREAD
-    pf->set_mass_spread(PARTICLE_FACTORY_MASS_SPREAD);
-  #endif
-  #ifdef PARTICLE_FACTORY_TERMINAL_VELOCITY_BASE
-    pf->set_terminal_velocity_base(PARTICLE_FACTORY_TERMINAL_VELOCITY_BASE);
-  #endif
-  #ifdef PARTICLE_FACTORY_TERMINAL_VELOCITY_SPREAD
-    pf->set_terminal_velocity_spread(PARTICLE_FACTORY_TERMINAL_VELOCITY_SPREAD);
-  #endif
-
-  #if defined ZSPIN_PARTICLES
-    #ifdef ZSPIN_PARTICLES_INITIAL_ANGLE
-      pf->set_initial_angle(ZSPIN_PARTICLES_INITIAL_ANGLE);
-    #endif
-    #ifdef ZSPIN_PARTICLES_FINAL_ANGLE
-      pf->set_final_angle(ZSPIN_PARTICLES_FINAL_ANGLE);
-    #endif
-    #ifdef ZSPIN_PARTICLES_INITIAL_ANGLE_SPREAD
-      pf->set_initial_angle_spread(ZSPIN_PARTICLES_INITIAL_ANGLE_SPREAD);
-    #endif
-    #ifdef ZSPIN_PARTICLES_FINAL_ANGLE_SPREAD
-      pf->set_final_angle_spread(ZSPIN_PARTICLES_FINAL_ANGLE_SPREAD);
-    #endif
-  #elif defined ORIENTED_PARTICLES
-  #endif
-
-  // emitter setup
-  #ifdef EMITTER_AMPLITUDE
-    pe->set_amplitude(EMITTER_AMPLITUDE);
-  #endif
-  #ifdef EMITTER_AMPLITUDE_SPREAD
-    pe->set_amplitude_spread(EMITTER_AMPLITUDE_SPREAD);
-  #endif
-  #ifdef EMITTER_OFFSET_FORCE
-    pe->set_offset_force(EMITTER_OFFSET_FORCE);
-  #endif
-
-  #if defined EMISSION_TYPE_EXPLICIT
-    pe->set_emission_type(BaseParticleEmitter::ET_EXPLICIT);
-    #ifdef EXPLICIT_LAUNCH_VEC
-    pe->set_explicit_launch_vector(EXPLICIT_LAUNCH_VEC);
-    #endif
-  #elif defined EMISSION_TYPE_RADIATE
-    pe->set_emission_type(BaseParticleEmitter::ET_RADIATE);
-    #ifdef RADIATE_ORIGIN
-      pe->set_radiate_origin(RADIATE_ORIGIN);
-    #endif
-  #elif defined EMISSION_TYPE_CUSTOM
-    pe->set_emission_type(BaseParticleEmitter::ET_CUSTOM);
-  #endif
-
-  #ifdef BOX_EMITTER
-    #ifdef BOX_EMITTER_MINBOUND
-      pe->set_min_bound(BOX_EMITTER_MINBOUND);
-    #endif
-    #ifdef BOX_EMITTER_MAXBOUND
-      pe->set_max_bound(BOX_EMITTER_MAXBOUND);
-    #endif
-  #elif defined DISC_EMITTER
-    #ifdef DISC_EMITTER_RADIUS
-      pe->set_radius(DISC_EMITTER_RADIUS);
-    #endif
-    #ifdef EMISSION_TYPE_CUSTOM
-      #ifdef DISC_EMITTER_OUTER_ANGLE
-        pe->set_outer_angle(DISC_EMITTER_OUTER_ANGLE);
-      #endif
-      #ifdef DISC_EMITTER_INNER_ANGLE
-        pe->set_inner_angle(DISC_EMITTER_INNER_ANGLE);
-      #endif
-      #ifdef DISC_EMITTER_OUTER_MAGNITUDE
-        pe->set_outer_magnitude(DISC_EMITTER_OUTER_MAGNITUDE);
-      #endif
-      #ifdef DISC_EMITTER_INNER_MAGNITUDE
-        pe->set_inner_magnitude(DISC_EMITTER_INNER_MAGNITUDE);
-      #endif
-      #ifdef DISC_EMITTER_CUBIC_LERPING
-        pe->set_cubic_lerping(DISC_EMITTER_CUBIC_LERPING);
-      #endif
-    #endif
-  #elif defined LINE_EMITTER
-    #ifdef LINE_EMITTER_ENDPOINT1
-      pe->set_endpoint1(LINE_EMITTER_ENDPOINT1);
-    #endif
-    #ifdef LINE_EMITTER_ENDPOINT2
-      pe->set_endpoint2(LINE_EMITTER_ENDPOINT2);
-    #endif
-  #elif defined POINT_EMITTER
-    #ifdef POINT_EMITTER_LOCATION
-      pe->set_location(POINT_EMITTER_LOCATION);
-    #endif
-  #elif defined RECTANGLE_EMITTER
-    #ifdef RECTANGLE_EMITTER_MINBOUND
-      pe->set_min_bound(RECTANGLE_EMITTER_MINBOUND);
-    #endif
-    #ifdef RECTANGLE_EMITTER_MAXBOUND
-      pe->set_max_bound(RECTANGLE_EMITTER_MAXBOUND);
-    #endif
-  #elif defined RING_EMITTER
-    #ifdef RING_EMITTER_RADIUS
-      pe->set_radius(RING_EMITTER_RADIUS);
-    #endif
-    #ifdef EMISSION_TYPE_CUSTOM
-      #ifdef RING_EMITTER_ANGLE
-        pe->set_aoe(RING_EMITTER_ANGLE);
-      #endif
-    #endif
-  #elif defined SPHERE_SURFACE_EMITTER
-    #ifdef SPHERE_SURFACE_EMITTER_RADIUS
-      pe->set_radius(SPHERE_SURFACE_EMITTER_RADIUS);
-    #endif
-  #elif defined TANGENT_RING_EMITTER
-    #ifdef TANGENT_RING_EMITTER_RADIUS
-      pe->set_radius(TANGENT_RING_EMITTER_RADIUS);
-    #endif
-  #elif defined SPHERE_VOLUME_EMITTER
-    #ifdef SPHERE_VOLUME_EMITTER_RADIUS
-      pe->set_radius(SPHERE_VOLUME_EMITTER_RADIUS);
-    #endif
-  #endif // emitter type
-
-  // system setup
-  particle_system->set_birth_rate(PARTICLE_SYSTEM_BIRTH_RATE);
-  particle_system->set_litter_size(PARTICLE_SYSTEM_LITTER_SIZE);
-  particle_system->set_litter_spread(PARTICLE_SYSTEM_LITTER_SPREAD);
-  particle_system->set_emitter(pe);
-  particle_system->set_renderer(pr);
-  particle_system->set_factory(pf);
-
-  particle_system->set_render_parent(render);
-
-  pn->add_physical(particle_system);
-  new RenderRelation(render, pn);
-
-#ifdef WIND_FORCE
-  fn->add_force(wind_force);
-#endif
-#ifdef NOISE_FORCE
-  fn->add_force(noise_force);
-#endif
-  new RenderRelation(render, fn);
-
-  physics_manager.attach_linear_integrator(new LinearEulerIntegrator);
-  physics_manager.attach_physical(particle_system);
-#ifdef WIND_FORCE
-  physics_manager.add_linear_force(wind_force);
-#endif
-
-#ifdef PARTICLE_SYSTEM_MANAGER_FRAME_STEPPING
-  ps_manager.set_frame_stepping(PARTICLE_SYSTEM_MANAGER_FRAME_STEPPING);
-#endif
-  ps_manager.attach_particlesystem(particle_system);
-
-#ifdef NOISE_FORCE
-  particle_system->add_linear_force(noise_force);
-#endif
-
-  nout << "Added particles." << endl;
-  event_handler.add_hook("NewFrame", event_csn_update);
-}
-
-static void set_pool_size(int size) {
-  nout << "setting pool size to " << size << endl;
-  particle_system->set_pool_size(size);
-}
-
-static void
-event_more_particles(CPT_Event) {
-  static int index = 0;
-  static int sizes[] = {
-    10,
-    999,
-    998,
-    999,
-    1000,
-    1001,
-    1002,
-    1003,
-    1004,
-    1000,
-    16*1000,
-    4*1000,
-    3*1000,
-    2*1000,
-    1000,
-    0,
-  };
-
-  if(!particles_added) return;
-
-  if (0 == sizes[index]) index = 0;
-  set_pool_size(sizes[index]);
-  index++;
-}
-
-static void
-event_switch_particle_factory_type(CPT_Event) {
-  static int index = 0;
-
-  if(!particles_added) return;
-
-  cout << "Switching to a";
-
-  switch (index) {
-    case 0:
-      cout << " point";
-      pf = new PointParticleFactory;
-      particle_system->set_factory(pf);
-      break;
-    case 1:
-      cout << " z-spin";
-      pf = new ZSpinParticleFactory;
-      particle_system->set_factory(pf);
-      break;
-    case 2:
-      cout << "n oriented";
-      pf = new OrientedParticleFactory;
-      particle_system->set_factory(pf);
-      break;
-  }
-
-  cout << " particle factory" << endl;
-
-  index++;
-  if (index > 2) index = 0;
-}
-
-void demo_keys(EventHandler&) {
-  new RenderRelation( lights, dlight );
-  have_dlight = true;
-
-  event_handler.add_hook("p", event_add_particles);
-  event_handler.add_hook("m", event_more_particles);
-  event_handler.add_hook(",", event_switch_particle_factory_type);
-}
-
-int main(int argc, char *argv[]) {
-  define_keys = &demo_keys;
-
-#ifdef SPRITE_PARTICLE_RENDERER
-  texture = TexturePool::load_texture(SPRITE_PARTICLE_RENDERER_TEXTURE_FILE);
-#endif
-
-  return framework_main(argc, argv);
-}