Kaynağa Gözat

add CollisionVisualizer

David Rose 23 yıl önce
ebeveyn
işleme
510756c0df
31 değiştirilmiş dosya ile 1081 ekleme ve 251 silme
  1. 11 3
      panda/src/collide/Sources.pp
  2. 0 1
      panda/src/collide/collide_composite1.cxx
  3. 2 3
      panda/src/collide/collide_composite2.cxx
  4. 27 0
      panda/src/collide/collisionEntry.I
  5. 4 0
      panda/src/collide/collisionEntry.h
  6. 10 28
      panda/src/collide/collisionPlane.cxx
  7. 4 11
      panda/src/collide/collisionPlane.h
  8. 34 47
      panda/src/collide/collisionPolygon.cxx
  9. 6 14
      panda/src/collide/collisionPolygon.h
  10. 3 4
      panda/src/collide/collisionRay.cxx
  11. 2 4
      panda/src/collide/collisionRay.h
  12. 30 0
      panda/src/collide/collisionRecorder.I
  13. 104 0
      panda/src/collide/collisionRecorder.cxx
  14. 84 0
      panda/src/collide/collisionRecorder.h
  15. 3 4
      panda/src/collide/collisionSegment.cxx
  16. 2 4
      panda/src/collide/collisionSegment.h
  17. 85 25
      panda/src/collide/collisionSolid.cxx
  18. 13 18
      panda/src/collide/collisionSolid.h
  19. 17 24
      panda/src/collide/collisionSphere.cxx
  20. 8 13
      panda/src/collide/collisionSphere.h
  21. 39 0
      panda/src/collide/collisionTraverser.I
  22. 68 2
      panda/src/collide/collisionTraverser.cxx
  23. 22 1
      panda/src/collide/collisionTraverser.h
  24. 29 0
      panda/src/collide/collisionVisualizer.I
  25. 291 0
      panda/src/collide/collisionVisualizer.cxx
  26. 111 0
      panda/src/collide/collisionVisualizer.h
  27. 10 3
      panda/src/collide/config_collide.cxx
  28. 2 2
      panda/src/collide/config_collide.h
  29. 45 22
      panda/src/pgraph/cullTraverserData.I
  30. 8 11
      panda/src/pgraph/cullTraverserData.cxx
  31. 7 7
      panda/src/pgraph/cullTraverserData.h

+ 11 - 3
panda/src/collide/Sources.pp

@@ -21,10 +21,13 @@
     collisionNode.I collisionNode.h \
     collisionPlane.I collisionPlane.h  \
     collisionPolygon.I collisionPolygon.h collisionRay.I  \
-    collisionRay.h collisionSegment.I collisionSegment.h  \
+    collisionRay.h \
+    collisionRecorder.I collisionRecorder.h \
+    collisionSegment.I collisionSegment.h  \
     collisionSolid.I collisionSolid.h collisionSphere.I  \
     collisionSphere.h \
     collisionTraverser.I collisionTraverser.h  \
+    collisionVisualizer.I collisionVisualizer.h \
     config_collide.h
     
  #define INCLUDED_SOURCES \
@@ -38,9 +41,12 @@
     collisionLevelState.cxx \
     collisionNode.cxx \
     collisionPlane.cxx  \
-    collisionPolygon.cxx collisionRay.cxx collisionSegment.cxx  \
+    collisionPolygon.cxx collisionRay.cxx \
+    collisionRecorder.cxx \
+    collisionSegment.cxx  \
     collisionSolid.cxx collisionSphere.cxx  \
     collisionTraverser.cxx \
+    collisionVisualizer.cxx \
     config_collide.cxx 
 
   #define INSTALL_HEADERS \
@@ -55,10 +61,12 @@
     collisionNode.I collisionNode.h \
     collisionPlane.I collisionPlane.h \
     collisionPolygon.I collisionPolygon.h collisionRay.I collisionRay.h \
+    collisionRecorder.I collisionRecorder.h \
     collisionSegment.I collisionSegment.h \
     collisionSolid.I collisionSolid.h collisionSphere.I \
     collisionSphere.h \
-    collisionTraverser.I collisionTraverser.h
+    collisionTraverser.I collisionTraverser.h \
+    collisionVisualizer.I collisionVisualizer.h
 
   #define IGATESCAN all
 

+ 0 - 1
panda/src/collide/collide_composite1.cxx

@@ -1,4 +1,3 @@
-
 #include "config_collide.cxx"
 #include "collisionEntry.cxx"
 #include "collisionHandler.cxx"

+ 2 - 3
panda/src/collide/collide_composite2.cxx

@@ -1,10 +1,9 @@
-
 #include "collisionPlane.cxx"
 #include "collisionPolygon.cxx"
 #include "collisionRay.cxx"
+#include "collisionRecorder.cxx"
 #include "collisionSegment.cxx"
 #include "collisionSolid.cxx"
 #include "collisionSphere.cxx"
 #include "collisionTraverser.cxx"
-
-
+#include "collisionVisualizer.cxx"

+ 27 - 0
panda/src/collide/collisionEntry.I

@@ -412,3 +412,30 @@ get_from_depth() const {
   nassertr(has_from_depth(), 0.0);
   return _from_depth;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionEntry::test_intersection
+//       Access: Private
+//  Description: This is intended to be called only by the
+//               CollisionTraverser.  It requests the CollisionEntry
+//               to start the intersection test between the from and
+//               into solids stored within it, passing the result (if
+//               positive) to the indicated CollisionHandler.
+////////////////////////////////////////////////////////////////////
+INLINE void CollisionEntry::
+test_intersection(CollisionHandler *record, 
+                  const CollisionTraverser *trav) const {
+  PT(CollisionEntry) result = get_from()->test_intersection(*this);
+#ifdef DO_COLLISION_RECORDING
+  if (trav->has_recorder()) {
+    if (result != (CollisionEntry *)NULL) {
+      trav->get_recorder()->collision_tested(*result, true);
+    } else {
+      trav->get_recorder()->collision_tested(*this, false);
+    }
+  }
+#endif  // DO_COLLISION_RECORDING
+  if (result != (CollisionEntry *)NULL) {
+    record->add_entry(result);
+  }
+}

+ 4 - 0
panda/src/collide/collisionEntry.h

@@ -21,8 +21,10 @@
 
 #include "pandabase.h"
 
+#include "collisionTraverser.h"
 #include "collisionSolid.h"
 #include "collisionNode.h"
+#include "collisionRecorder.h"
 
 #include "typedReferenceCount.h"
 #include "luse.h"
@@ -91,6 +93,8 @@ PUBLISHED:
   INLINE float get_from_depth() const;
 
 private:
+  INLINE void test_intersection(CollisionHandler *record, 
+                                const CollisionTraverser *trav) const;
   void compute_from_surface_normal();
 
   CPT(CollisionSolid) _from;

+ 10 - 28
panda/src/collide/collisionPlane.cxx

@@ -46,20 +46,6 @@ make_copy() {
   return new CollisionPlane(*this);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CollisionPlane::test_intersection
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-int CollisionPlane::
-test_intersection(CollisionHandler *, const CollisionEntry &,
-                  const CollisionSolid *) const {
-  // Planes cannot currently be intersected from, only into.  Do not
-  // add a CollisionPlane to a CollisionTraverser.
-  nassertr(false, 0);
-  return 0;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPlane::xform
 //       Access: Public, Virtual
@@ -117,9 +103,8 @@ recompute_bound() {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionPlane::
-test_intersection_from_sphere(CollisionHandler *record,
-                              const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionPlane::
+test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), 0);
 
@@ -131,7 +116,7 @@ test_intersection_from_sphere(CollisionHandler *record,
   float dist = dist_to_plane(from_center);
   if (dist > from_radius) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -147,10 +132,9 @@ test_intersection_from_sphere(CollisionHandler *record,
   new_entry->set_into_surface_normal(get_normal());
   new_entry->set_from_surface_normal(from_normal);
   new_entry->set_from_depth(from_depth);
-  new_entry->set_into_intersection_point(from_center);
+  new_entry->set_into_intersection_point(from_center - get_normal() * dist);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -158,9 +142,8 @@ test_intersection_from_sphere(CollisionHandler *record,
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionPlane::
-test_intersection_from_ray(CollisionHandler *record,
-                           const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionPlane::
+test_intersection_from_ray(const CollisionEntry &entry) const {
   const CollisionRay *ray;
   DCAST_INTO_R(ray, entry.get_from(), 0);
 
@@ -170,12 +153,12 @@ test_intersection_from_ray(CollisionHandler *record,
   float t;
   if (!_plane.intersects_line(t, from_origin, from_direction)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (t < 0.0f) {
     // The intersection point is before the start of the ray.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -189,8 +172,7 @@ test_intersection_from_ray(CollisionHandler *record,
   new_entry->set_into_surface_normal(get_normal());
   new_entry->set_into_intersection_point(into_intersection_point);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 11
panda/src/collide/collisionPlane.h

@@ -41,11 +41,6 @@ PUBLISHED:
 public:
   virtual CollisionSolid *make_copy();
 
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const;
-
   virtual void xform(const LMatrix4f &mat);
   virtual LPoint3f get_collision_origin() const;
 
@@ -62,12 +57,10 @@ protected:
   virtual BoundingVolume *recompute_bound();
 
 protected:
-  virtual int
-  test_intersection_from_sphere(CollisionHandler *record,
-                                const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_ray(CollisionHandler *record,
-                             const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_ray(const CollisionEntry &entry) const;
 
   virtual void fill_viz_geom();
 

+ 34 - 47
panda/src/collide/collisionPolygon.cxx

@@ -120,20 +120,6 @@ verify_points(const LPoint3f *begin, const LPoint3f *end) {
   return all_ok;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CollisionPolygon::test_intersection
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-int CollisionPolygon::
-test_intersection(CollisionHandler *, const CollisionEntry &,
-                  const CollisionSolid *into) const {
-  // Polygons cannot currently be intersected from, only into.  Do not
-  // add a CollisionPolygon to a CollisionTraverser.
-  nassertr(false, 0);
-  return 0;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::xform
 //       Access: Public, Virtual
@@ -227,14 +213,15 @@ recompute_bound() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::test_intersection_from_sphere
-//       Access: Public, Virtual
-//  Description:
+//       Access: Protected, Virtual
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a sphere.
 ////////////////////////////////////////////////////////////////////
-int CollisionPolygon::
-test_intersection_from_sphere(CollisionHandler *record,
-                              const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionPolygon::
+test_intersection_from_sphere(const CollisionEntry &entry) const {
   if (_points.size() < 3) {
-    return 0;
+    return NULL;
   }
 
   const CollisionSphere *sphere;
@@ -255,7 +242,7 @@ test_intersection_from_sphere(CollisionHandler *record,
     // the same direction as the plane's normal.
     float dot = delta.dot(get_normal());
     if (dot > 0.0f) {
-      return 0;
+      return NULL;
     }
 
     if (IS_NEARLY_ZERO(dot)) {
@@ -289,7 +276,7 @@ test_intersection_from_sphere(CollisionHandler *record,
   float dist = dist_to_plane(from_center);
   if (dist > from_radius || dist < -from_radius) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   // Ok, we intersected the plane, but did we intersect the polygon?
@@ -351,7 +338,7 @@ test_intersection_from_sphere(CollisionHandler *record,
 
         } else {
           // No intersection.
-          return 0;
+          return NULL;
         }
       }
     }
@@ -376,22 +363,23 @@ test_intersection_from_sphere(CollisionHandler *record,
   new_entry->set_into_surface_normal(get_normal());
   new_entry->set_from_surface_normal(from_normal);
   new_entry->set_from_depth(from_depth);
-  new_entry->set_into_intersection_point(from_center);
 
-  record->add_entry(new_entry);
-  return 1;
+  new_entry->set_into_intersection_point(from_center - get_normal() * dist);
+
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::test_intersection_from_ray
-//       Access: Public, Virtual
-//  Description:
+//       Access: Protected, Virtual
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a ray.
 ////////////////////////////////////////////////////////////////////
-int CollisionPolygon::
-test_intersection_from_ray(CollisionHandler *record,
-                           const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionPolygon::
+test_intersection_from_ray(const CollisionEntry &entry) const {
   if (_points.size() < 3) {
-    return 0;
+    return NULL;
   }
 
   const CollisionRay *ray;
@@ -403,18 +391,18 @@ test_intersection_from_ray(CollisionHandler *record,
   float t;
   if (!get_plane().intersects_line(t, from_origin, from_direction)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (t < 0.0f) {
     // The intersection point is before the start of the ray.
-    return 0;
+    return NULL;
   }
 
   LPoint3f plane_point = from_origin + t * from_direction;
   if (!is_inside(to_2d(plane_point))) {
     // Outside the polygon's perimeter.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -427,20 +415,20 @@ test_intersection_from_ray(CollisionHandler *record,
   new_entry->set_into_surface_normal(get_normal());
   new_entry->set_into_intersection_point(plane_point);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::test_intersection_from_segment
 //       Access: Public, Virtual
-//  Description:
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a segment.
 ////////////////////////////////////////////////////////////////////
-int CollisionPolygon::
-test_intersection_from_segment(CollisionHandler *record,
-                               const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionPolygon::
+test_intersection_from_segment(const CollisionEntry &entry) const {
   if (_points.size() < 3) {
-    return 0;
+    return NULL;
   }
 
   const CollisionSegment *segment;
@@ -453,19 +441,19 @@ test_intersection_from_segment(CollisionHandler *record,
   float t;
   if (!get_plane().intersects_line(t, from_a, from_direction)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (t < 0.0f || t > 1.0f) {
     // The intersection point is before the start of the segment or
     // after the end of the segment.
-    return 0;
+    return NULL;
   }
 
   LPoint3f plane_point = from_a + t * from_direction;
   if (!is_inside(to_2d(plane_point))) {
     // Outside the polygon's perimeter.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -478,8 +466,7 @@ test_intersection_from_segment(CollisionHandler *record,
   new_entry->set_into_surface_normal(get_normal());
   new_entry->set_into_intersection_point(plane_point);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 14
panda/src/collide/collisionPolygon.h

@@ -49,11 +49,6 @@ public:
   static bool verify_points(const LPoint3f *begin, const LPoint3f *end);
 
 
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const;
-
   virtual void xform(const LMatrix4f &mat);
   virtual LPoint3f get_collision_origin() const;
 
@@ -64,15 +59,12 @@ protected:
   virtual BoundingVolume *recompute_bound();
 
 protected:
-  virtual int
-  test_intersection_from_sphere(CollisionHandler *record,
-                                const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_ray(CollisionHandler *record,
-                             const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_segment(CollisionHandler *record,
-                                 const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_ray(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_segment(const CollisionEntry &entry) const;
 
   virtual void fill_viz_geom();
 

+ 3 - 4
panda/src/collide/collisionRay.cxx

@@ -49,10 +49,9 @@ make_copy() {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionRay::
-test_intersection(CollisionHandler *record, const CollisionEntry &entry,
-                  const CollisionSolid *into) const {
-  return into->test_intersection_from_ray(record, entry);
+PT(CollisionEntry) CollisionRay::
+test_intersection(const CollisionEntry &entry) const {
+  return entry.get_into()->test_intersection_from_ray(entry);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 4
panda/src/collide/collisionRay.h

@@ -44,10 +44,8 @@ public:
   INLINE CollisionRay(const CollisionRay &copy);
   virtual CollisionSolid *make_copy();
 
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const;
+  virtual PT(CollisionEntry)
+  test_intersection(const CollisionEntry &entry) const;
 
   virtual void xform(const LMatrix4f &mat);
   virtual LPoint3f get_collision_origin() const;

+ 30 - 0
panda/src/collide/collisionRecorder.I

@@ -0,0 +1,30 @@
+// Filename: collisionRecorder.I
+// Created by:  drose (17Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::get_type_index
+//       Access: Published
+//  Description: Duplicates the functionality of
+//               TypedObject::get_type_index().
+////////////////////////////////////////////////////////////////////
+INLINE int CollisionRecorder::
+get_type_index() const {
+  return get_type().get_index();
+}
+

+ 104 - 0
panda/src/collide/collisionRecorder.cxx

@@ -0,0 +1,104 @@
+// Filename: collisionRecorder.cxx
+// Created by:  drose (16Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "collisionRecorder.h"
+#include "collisionTraverser.h"
+
+#ifdef DO_COLLISION_RECORDING
+
+TypeHandle CollisionRecorder::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+CollisionRecorder::
+CollisionRecorder() {
+  _num_missed = 0;
+  _num_detected = 0;
+  _trav = (CollisionTraverser *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CollisionRecorder::
+~CollisionRecorder() {
+  if (_trav != (CollisionTraverser *)NULL) {
+    _trav->clear_recorder();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CollisionRecorder::
+output(ostream &out) const {
+  out << "tested " << _num_missed + _num_detected << ", detected "
+      << _num_detected << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::begin_traversal
+//       Access: Public, Virtual
+//  Description: This method is called at the beginning of a
+//               CollisionTraverser::traverse() call.  It is provided
+//               as a hook for the derived class to reset its state as
+//               appropriate.
+////////////////////////////////////////////////////////////////////
+void CollisionRecorder::
+begin_traversal() {
+  _num_missed = 0;
+  _num_detected = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::collision_tested
+//       Access: Public, Virtual
+//  Description: This method is called when a pair of collision solids
+//               have passed all bounding-volume tests and have been
+//               tested for a collision.  The detected value is set
+//               true if a collision was detected, false otherwise.
+////////////////////////////////////////////////////////////////////
+void CollisionRecorder::
+collision_tested(const CollisionEntry &entry, bool detected) {
+  if (detected) {
+    _num_detected++;
+  } else {
+    _num_missed++;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionRecorder::end_traversal
+//       Access: Public, Virtual
+//  Description: This method is called at the end of a
+//               CollisionTraverser::traverse() call.  It is provided
+//               as a hook for the derived class to finalize its state
+//               as appropriate.
+////////////////////////////////////////////////////////////////////
+void CollisionRecorder::
+end_traversal() {
+}
+
+#endif  // DO_COLLISION_RECORDING

+ 84 - 0
panda/src/collide/collisionRecorder.h

@@ -0,0 +1,84 @@
+// Filename: collisionRecorder.h
+// Created by:  drose (16Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef COLLISIONRECORDER_H
+#define COLLISIONRECORDER_H
+
+#include "pandabase.h"
+#include "typeHandle.h"
+
+class CollisionTraverser;
+
+#ifdef DO_COLLISION_RECORDING
+
+////////////////////////////////////////////////////////////////////
+//       Class : CollisionRecorder
+// Description : This class is used to help debug the work the
+//               collisions system is doing.  It is a virtual base
+//               class that just provides an interface for recording
+//               collisions tested and detected each frame.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CollisionRecorder {
+protected:
+  CollisionRecorder();
+public:
+  virtual ~CollisionRecorder();
+
+PUBLISHED:
+  void output(ostream &out) const;
+
+public:
+  virtual void begin_traversal();
+  virtual void collision_tested(const CollisionEntry &entry, bool detected);
+  virtual void end_traversal();
+
+private:
+  int _num_missed;
+  int _num_detected;
+  CollisionTraverser *_trav;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+
+  static void init_type() {
+    register_type(_type_handle, "CollisionRecorder");
+  }
+
+PUBLISHED:
+  // We have to publish this explicitly because the CollisionRecorder
+  // object does not inherit from TypedObject.
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  INLINE int get_type_index() const;
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class CollisionTraverser;
+};
+
+#include "collisionRecorder.I"
+
+#endif  // DO_COLLISION_RECORDING
+
+
+#endif
+

+ 3 - 4
panda/src/collide/collisionSegment.cxx

@@ -51,10 +51,9 @@ make_copy() {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionSegment::
-test_intersection(CollisionHandler *record, const CollisionEntry &entry,
-                  const CollisionSolid *into) const {
-  return into->test_intersection_from_segment(record, entry);
+PT(CollisionEntry) CollisionSegment::
+test_intersection(const CollisionEntry &entry) const {
+  return entry.get_into()->test_intersection_from_segment(entry);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 4
panda/src/collide/collisionSegment.h

@@ -47,10 +47,8 @@ public:
   INLINE CollisionSegment(const CollisionSegment &copy);
   virtual CollisionSolid *make_copy();
 
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const;
+  virtual PT(CollisionEntry)
+  test_intersection(const CollisionEntry &entry) const;
 
   virtual void xform(const LMatrix4f &mat);
   virtual LPoint3f get_collision_origin() const;

+ 85 - 25
panda/src/collide/collisionSolid.cxx

@@ -69,6 +69,21 @@ CollisionSolid::
 ~CollisionSolid() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::test_intersection
+//       Access: Public, Virtual
+//  Description: Tests for a collision between this object (which is
+//               also the "from" object in the entry) and the "into"
+//               object.  If a collision is detected, returns a new
+//               CollisionEntry object that records the collision;
+//               otherwise, returns NULL.
+////////////////////////////////////////////////////////////////////
+PT(CollisionEntry) CollisionSolid::
+test_intersection(const CollisionEntry &) const {
+  report_undefined_from_intersection(get_type());
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::get_viz
 //       Access: Public
@@ -78,15 +93,15 @@ CollisionSolid::
 //               made visible.
 ////////////////////////////////////////////////////////////////////
 GeomNode *CollisionSolid::
-get_viz() {
+get_viz() const {
   if (_viz_geom_stale) {
     if (_viz_geom == (GeomNode *)NULL) {
-      _viz_geom = new GeomNode("viz");
+      ((CollisionSolid *)this)->_viz_geom = new GeomNode("viz");
     } else {
       _viz_geom->remove_all_geoms();
     }
-    fill_viz_geom();
-    _viz_geom_stale = false;
+    ((CollisionSolid *)this)->fill_viz_geom();
+    ((CollisionSolid *)this)->_viz_geom_stale = false;
   }
   return _viz_geom;
 }
@@ -114,42 +129,62 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::test_intersection_from_sphere
 //       Access: Protected, Virtual
-//  Description:
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a sphere.
 ////////////////////////////////////////////////////////////////////
-int CollisionSolid::
-test_intersection_from_sphere(CollisionHandler *,
-                              const CollisionEntry &) const {
+PT(CollisionEntry) CollisionSolid::
+test_intersection_from_sphere(const CollisionEntry &) const {
   report_undefined_intersection_test(CollisionSphere::get_class_type(),
                                      get_type());
-  return 0;
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::test_intersection_from_ray
 //       Access: Protected, Virtual
-//  Description:
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a ray.
 ////////////////////////////////////////////////////////////////////
-int CollisionSolid::
-test_intersection_from_ray(CollisionHandler *,
-                           const CollisionEntry &) const {
+PT(CollisionEntry) CollisionSolid::
+test_intersection_from_ray(const CollisionEntry &) const {
   report_undefined_intersection_test(CollisionRay::get_class_type(),
                                      get_type());
-  return 0;
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::test_intersection_from_segment
 //       Access: Protected, Virtual
-//  Description:
+//  Description: This is part of the double-dispatch implementation of
+//               test_intersection().  It is called when the "from"
+//               object is a segment.
 ////////////////////////////////////////////////////////////////////
-int CollisionSolid::
-test_intersection_from_segment(CollisionHandler *,
-                               const CollisionEntry &) const {
+PT(CollisionEntry) CollisionSolid::
+test_intersection_from_segment(const CollisionEntry &) const {
   report_undefined_intersection_test(CollisionSegment::get_class_type(),
                                      get_type());
-  return 0;
+  return NULL;
 }
 
+#ifndef NDEBUG
+class CollisionSolidUndefinedPair {
+public:
+  CollisionSolidUndefinedPair(TypeHandle a, TypeHandle b) :
+    _a(a), _b(b)
+  {}
+  bool operator < (const CollisionSolidUndefinedPair &other) const {
+    if (_a != other._a) {
+      return _a < other._a;
+    }
+    return _b < other._b;
+  }
+
+  TypeHandle _a;
+  TypeHandle _b;
+};
+#endif  // NDEBUG
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::report_undefined_intersection_test
@@ -161,22 +196,47 @@ test_intersection_from_segment(CollisionHandler *,
 void CollisionSolid::
 report_undefined_intersection_test(TypeHandle from_type, TypeHandle into_type) {
 #ifndef NDEBUG
-  typedef pmap<TypeHandle, TypeHandle> Reported;
+  typedef pset<CollisionSolidUndefinedPair> Reported;
   static Reported reported;
 
-  if (reported.insert(Reported::value_type(from_type, into_type)).second) {
+  if (reported.insert(CollisionSolidUndefinedPair(from_type, into_type)).second) {
     collide_cat.error()
       << "Invalid attempt to detect collision from " << from_type << " into "
-      << into_type << "!\n"
+      << into_type << "!\n\n"
 
-      "This means that a " << from_type << " object attempted to test for a\n"
+      "This means that a " << from_type << " object attempted to test for an\n"
       "intersection into a " << into_type << " object.  This intersection\n"
       "test has not yet been defined; it is possible the " << into_type << "\n"
       "object is not intended to be collidable.  Consider calling\n"
       "set_into_collide_mask(0) on the " << into_type << " object, or\n"
-      "set_from_collide_mask(0) on the " << from_type << " object.\n";
+      "set_from_collide_mask(0) on the " << from_type << " object.\n\n";
+  }
+#endif  // NDEBUG
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::report_undefined_from_intersection
+//       Access: Protected, Static
+//  Description: Outputs a message the first time an intersection test
+//               is attempted that isn't defined, and explains a bit
+//               about what it means.
+////////////////////////////////////////////////////////////////////
+void CollisionSolid::
+report_undefined_from_intersection(TypeHandle from_type) {
+#ifndef NDEBUG
+  typedef pset<TypeHandle> Reported;
+  static Reported reported;
+
+  if (reported.insert(from_type).second) {
+    collide_cat.error()
+      << "Invalid attempt to detect collision from " << from_type << "!\n\n"
+      
+      "This means that a " << from_type << " object was added to a\n"
+      "CollisionTraverser as if it were a colliding object.  However,\n"
+      "no implementation for this kind of object has yet been defined\n"
+      "to collide with other objects.\n\n";
   }
-#endif
+#endif  // NDEBUG
 }
 
 ////////////////////////////////////////////////////////////////////

+ 13 - 18
panda/src/collide/collisionSolid.h

@@ -62,33 +62,28 @@ PUBLISHED:
   INLINE bool is_tangible() const;
 
 public:
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const=0;
+  virtual PT(CollisionEntry)
+  test_intersection(const CollisionEntry &entry) const;
 
   virtual void xform(const LMatrix4f &mat)=0;
 
-  GeomNode *get_viz();
+  GeomNode *get_viz() const;
 
 PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 protected:
-  virtual int
-  test_intersection_from_sphere(CollisionHandler *record,
-                                const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_ray(CollisionHandler *record,
-                             const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_segment(CollisionHandler *record,
-                                 const CollisionEntry &entry) const;
-
-  static void
-  report_undefined_intersection_test(TypeHandle from_type,
-                                     TypeHandle into_type);
+  virtual PT(CollisionEntry)
+  test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_ray(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_segment(const CollisionEntry &entry) const;
+
+  static void report_undefined_intersection_test(TypeHandle from_type,
+                                                 TypeHandle into_type);
+  static void report_undefined_from_intersection(TypeHandle from_type);
 
   INLINE void mark_viz_stale();
   virtual void fill_viz_geom();

+ 17 - 24
panda/src/collide/collisionSphere.cxx

@@ -49,10 +49,9 @@ make_copy() {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionSphere::
-test_intersection(CollisionHandler *record, const CollisionEntry &entry,
-                  const CollisionSolid *into) const {
-  return into->test_intersection_from_sphere(record, entry);
+PT(CollisionEntry) CollisionSphere::
+test_intersection(const CollisionEntry &entry) const {
+  return entry.get_into()->test_intersection_from_sphere(entry);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -117,9 +116,8 @@ recompute_bound() {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionSphere::
-test_intersection_from_sphere(CollisionHandler *record,
-                              const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionSphere::
+test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), 0);
 
@@ -135,7 +133,7 @@ test_intersection_from_sphere(CollisionHandler *record,
   float dist2 = dot(vec, vec);
   if (dist2 > (into_radius + from_radius) * (into_radius + from_radius)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -160,8 +158,7 @@ test_intersection_from_sphere(CollisionHandler *record,
   // (especially including non-uniform scales) is rather complicated.
   new_entry->set_from_depth(into_depth);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -169,9 +166,8 @@ test_intersection_from_sphere(CollisionHandler *record,
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionSphere::
-test_intersection_from_ray(CollisionHandler *record,
-                           const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionSphere::
+test_intersection_from_ray(const CollisionEntry &entry) const {
   const CollisionRay *ray;
   DCAST_INTO_R(ray, entry.get_from(), 0);
 
@@ -181,12 +177,12 @@ test_intersection_from_ray(CollisionHandler *record,
   double t1, t2;
   if (!intersects_line(t1, t2, from_origin, from_direction)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (t2 < 0.0) {
     // Both intersection points are before the start of the ray.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -204,8 +200,7 @@ test_intersection_from_ray(CollisionHandler *record,
   }
   new_entry->set_into_intersection_point(into_intersection_point);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -213,9 +208,8 @@ test_intersection_from_ray(CollisionHandler *record,
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int CollisionSphere::
-test_intersection_from_segment(CollisionHandler *record,
-                               const CollisionEntry &entry) const {
+PT(CollisionEntry) CollisionSphere::
+test_intersection_from_segment(const CollisionEntry &entry) const {
   const CollisionSegment *segment;
   DCAST_INTO_R(segment, entry.get_from(), 0);
 
@@ -226,13 +220,13 @@ test_intersection_from_segment(CollisionHandler *record,
   double t1, t2;
   if (!intersects_line(t1, t2, from_a, from_direction)) {
     // No intersection.
-    return 0;
+    return NULL;
   }
 
   if (t2 < 0.0 || t1 > 1.0) {
     // Both intersection points are before the start of the segment or
     // after the end of the segment.
-    return 0;
+    return NULL;
   }
 
   if (collide_cat.is_debug()) {
@@ -254,8 +248,7 @@ test_intersection_from_segment(CollisionHandler *record,
   }
   new_entry->set_into_intersection_point(into_intersection_point);
 
-  record->add_entry(new_entry);
-  return 1;
+  return new_entry;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 8 - 13
panda/src/collide/collisionSphere.h

@@ -36,10 +36,8 @@ public:
   INLINE CollisionSphere(const CollisionSphere &copy);
   virtual CollisionSolid *make_copy();
 
-  virtual int
-  test_intersection(CollisionHandler *record,
-                    const CollisionEntry &entry,
-                    const CollisionSolid *into) const;
+  virtual PT(CollisionEntry)
+  test_intersection(const CollisionEntry &entry) const;
 
   virtual void xform(const LMatrix4f &mat);
   virtual LPoint3f get_collision_origin() const;
@@ -59,15 +57,12 @@ protected:
   virtual BoundingVolume *recompute_bound();
 
 protected:
-  virtual int
-  test_intersection_from_sphere(CollisionHandler *record,
-                                const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_ray(CollisionHandler *record,
-                             const CollisionEntry &entry) const;
-  virtual int
-  test_intersection_from_segment(CollisionHandler *record,
-                                 const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_ray(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_segment(const CollisionEntry &entry) const;
 
   virtual void fill_viz_geom();
 

+ 39 - 0
panda/src/collide/collisionTraverser.I

@@ -15,3 +15,42 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
+
+#ifdef DO_COLLISION_RECORDING
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::has_recorder
+//       Access: Public
+//  Description: Returns true if the CollisionTraverser has a
+//               CollisionRecorder object currently assigned, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool CollisionTraverser::
+has_recorder() const {
+  return _recorder != (CollisionRecorder *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::get_recorder
+//       Access: Public
+//  Description: Returns the CollisionRecorder currently assigned, or
+//               NULL if no recorder is assigned.
+////////////////////////////////////////////////////////////////////
+INLINE CollisionRecorder *CollisionTraverser::
+get_recorder() const {
+  return _recorder;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::clear_recorder
+//       Access: Public
+//  Description: Removes the CollisionRecorder from the traverser and
+//               restores normal low-overhead operation.
+////////////////////////////////////////////////////////////////////
+INLINE void CollisionTraverser::
+clear_recorder() {
+  set_recorder((CollisionRecorder *)NULL);
+}
+
+#endif  // DO_COLLISION_RECORDING

+ 68 - 2
panda/src/collide/collisionTraverser.cxx

@@ -20,6 +20,7 @@
 #include "collisionNode.h"
 #include "collisionEntry.h"
 #include "collisionPolygon.h"
+#include "collisionRecorder.h"
 #include "config_collide.h"
 
 #include "transformState.h"
@@ -40,6 +41,9 @@ PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions");
 ////////////////////////////////////////////////////////////////////
 CollisionTraverser::
 CollisionTraverser() {
+#ifdef DO_COLLISION_RECORDING
+  _recorder = (CollisionRecorder *)NULL;
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -49,6 +53,9 @@ CollisionTraverser() {
 ////////////////////////////////////////////////////////////////////
 CollisionTraverser::
 ~CollisionTraverser() {
+#ifdef DO_COLLISION_RECORDING
+  clear_recorder();
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -229,6 +236,12 @@ void CollisionTraverser::
 traverse(const NodePath &root) {
   PStatTimer timer(_collisions_pcollector);
 
+#ifdef DO_COLLISION_RECORDING
+  if (has_recorder()) {
+    get_recorder()->begin_traversal();
+  }
+#endif  // DO_COLLISION_RECORDING
+
   CollisionLevelState level_state(root);
   prepare_colliders(level_state);
 
@@ -262,7 +275,60 @@ traverse(const NodePath &root) {
       node->clear_velocity();
     }
   }
+
+#ifdef DO_COLLISION_RECORDING
+  if (has_recorder()) {
+    get_recorder()->end_traversal();
+  }
+#endif  // DO_COLLISION_RECORDING
+}
+
+#ifdef DO_COLLISION_RECORDING
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionTraverser::set_recorder
+//       Access: Public
+//  Description: Uses the indicated CollisionRecorder object to start
+//               recording the intersection tests made by each
+//               subsequent call to traverse() on this object.  A
+//               particular CollisionRecorder object can only record
+//               one traverser at a time; if this object has already
+//               been assigned to another traverser, that assignment
+//               is broken.
+//
+//               This is intended to be used in a debugging mode to
+//               try to determine what work is being performed by the
+//               collision traversal.  Usually, attaching a recorder
+//               will impose significant runtime overhead.
+//
+//               This does not transfer ownership of the
+//               CollisionRecorder pointer; maintenance of that
+//               remains the caller's responsibility.  If the
+//               CollisionRecorder is destructed, it will cleanly
+//               remove itself from the traverser.
+////////////////////////////////////////////////////////////////////
+void CollisionTraverser::
+set_recorder(CollisionRecorder *recorder) {
+  if (recorder != _recorder) {
+    // Remove the old recorder, if any.
+    if (_recorder != (CollisionRecorder *)NULL) {
+      nassertv(_recorder->_trav == this);
+      _recorder->_trav = (CollisionTraverser *)NULL;
+    }
+    
+    _recorder = recorder;
+    
+    // Tell the new recorder about his new owner.
+    if (_recorder != (CollisionRecorder *)NULL) {
+      nassertv(_recorder->_trav != this);
+      if (_recorder->_trav != (CollisionTraverser *)NULL) {
+        _recorder->_trav->clear_recorder();
+      }
+      nassertv(_recorder->_trav == (CollisionTraverser *)NULL);
+      _recorder->_trav = this;
+    }
+  }
 }
+#endif  // DO_COLLISION_RECORDING
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionTraverser::output
@@ -567,7 +633,7 @@ compare_collider_to_solid(CollisionEntry &entry,
     Colliders::const_iterator ci;
     ci = _colliders.find(entry.get_from_node());
     nassertv(ci != _colliders.end());
-    entry.get_from()->test_intersection((*ci).second, entry, entry.get_into());
+    entry.test_intersection((*ci).second, this);
   }
 }
 
@@ -604,7 +670,7 @@ compare_collider_to_geom(CollisionEntry &entry, Geom *geom,
         entry._into =
           new CollisionPolygon(coords[tris[i]], coords[tris[i + 1]],
                                coords[tris[i + 2]]);
-        entry.get_from()->test_intersection((*ci).second, entry, entry.get_into());
+        entry.test_intersection((*ci).second, this);
       }
     }
   }

+ 22 - 1
panda/src/collide/collisionTraverser.h

@@ -30,13 +30,23 @@
 #include "pset.h"
 
 class CollisionNode;
+class CollisionRecorder;
 class Geom;
 class NodePath;
 class CollisionEntry;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : CollisionTraverser
-// Description :
+// Description : This class manages the traversal through the scene
+//               graph to detect collisions.  It holds ownership of a
+//               number of collider objects, each of which is a
+//               CollisionNode and an associated CollisionHandler.
+//
+//               When traverse() is called, it begins at the indicated
+//               root and detects all collisions with any of its
+//               collider objects against nodes at or below the
+//               indicated root, calling the appropriate
+//               CollisionHandler for each detected collision.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA CollisionTraverser {
 PUBLISHED:
@@ -53,6 +63,13 @@ PUBLISHED:
 
   void traverse(const NodePath &root);
 
+#ifdef DO_COLLISION_RECORDING
+  void set_recorder(CollisionRecorder *recorder);
+  INLINE bool has_recorder() const;
+  INLINE CollisionRecorder *get_recorder() const;
+  INLINE void clear_recorder();
+#endif  // DO_COLLISION_RECORDING
+
   void output(ostream &out) const;
   void write(ostream &out, int indent_level) const;
 
@@ -88,6 +105,10 @@ private:
   typedef pmap<PT(CollisionHandler), int> Handlers;
   Handlers _handlers;
 
+#ifdef DO_COLLISION_RECORDING
+  CollisionRecorder *_recorder;
+#endif  // DO_COLLISION_RECORDING
+
   // Statistics
   static PStatCollector _collisions_pcollector;
 };

+ 29 - 0
panda/src/collide/collisionVisualizer.I

@@ -0,0 +1,29 @@
+// Filename: collisionVisualizer.I
+// Created by:  drose (17Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::SolidInfo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CollisionVisualizer::SolidInfo::
+SolidInfo() {
+  _detected_count = 0;
+  _missed_count = 0;
+}

+ 291 - 0
panda/src/collide/collisionVisualizer.cxx

@@ -0,0 +1,291 @@
+// Filename: collisionVisualizer.cxx
+// Created by:  drose (16Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "collisionVisualizer.h"
+#include "collisionEntry.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
+#include "renderState.h"
+#include "omniBoundingVolume.h"
+#include "depthOffsetAttrib.h"
+#include "colorScaleAttrib.h"
+#include "transparencyAttrib.h"
+
+
+#ifdef DO_COLLISION_RECORDING
+
+TypeHandle CollisionVisualizer::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+CollisionVisualizer::
+CollisionVisualizer(const string &name) : PandaNode(name) {
+  // We always want to render the CollisionVisualizer node itself
+  // (even if it doesn't appear to have any geometry within it).
+  set_bound(OmniBoundingVolume());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CollisionVisualizer::
+~CollisionVisualizer() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::clear
+//       Access: Published
+//  Description: Removes all the visualization data from a previous
+//               traversal and resets the visualizer to empty.
+////////////////////////////////////////////////////////////////////
+void CollisionVisualizer::
+clear() {
+  _data.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *CollisionVisualizer::
+make_copy() const {
+  return new CollisionVisualizer(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::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 CollisionVisualizer::
+has_cull_callback() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::cull_callback
+//       Access: Public, Virtual
+//  Description: If has_cull_callback() returns true, this function
+//               will be called during the cull traversal to perform
+//               any additional operations that should be performed at
+//               cull time.  This may include additional manipulation
+//               of render state or additional visible/invisible
+//               decisions, or any other arbitrary operation.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool CollisionVisualizer::
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  // Now we go through and actually draw our visualized collision solids.
+
+  Data::const_iterator di;
+  for (di = _data.begin(); di != _data.end(); ++di) {
+    const TransformState *net_transform = (*di).first;
+    const VizInfo &viz_info = (*di).second;
+
+    CullTraverserData xform_data(data);
+
+    // We don't want to inherit the transform state!  We ignore
+    // whatever transforms were above the CollisionVisualizer node; it
+    // always renders its objects according to their appropriate net
+    // transform.
+    xform_data._net_transform = TransformState::make_identity();
+    xform_data._render_transform = trav->get_render_transform();
+    xform_data.apply_transform_and_state(trav, net_transform,
+                                         RenderState::make_empty(),
+                                         RenderEffects::make_empty());
+
+    // Draw all the collision solids.
+    Solids::const_iterator si;
+    for (si = viz_info._solids.begin(); si != viz_info._solids.end(); ++si) {
+      const CollisionSolid *solid = (*si).first;
+      const SolidInfo &solid_info = (*si).second;
+      PandaNode *node = solid->get_viz();
+
+      CullTraverserData next_data(xform_data, node);
+
+      // We don't want to inherit the render state from above for
+      // these guys.  Instead, we choose the state according to
+      // whether a collision was detected or not.
+      if (solid_info._detected_count > 0) {
+        next_data._state = get_detected_state();
+      } else {
+        next_data._state = get_tested_state();
+      }
+
+      trav->traverse(next_data);
+    }
+
+    // Now draw all of the detected points.
+    if (!viz_info._points.empty()) {
+      CPT(RenderState) line_state = RenderState::make_empty();
+
+      PTA_Colorf colors;
+      colors.push_back(Colorf(1.0f, 0.0f, 0.0f, 1.0f));
+      colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+
+      Points::const_iterator pi;
+      for (pi = viz_info._points.begin(); pi != viz_info._points.end(); ++pi) {
+        const CollisionPoint &point = (*pi);
+        PT(GeomLine) line = new GeomLine;
+        
+        PTA_Vertexf verts;
+        verts.push_back(point._point);
+        verts.push_back(point._point + point._normal);
+        line->set_coords(verts);
+        line->set_colors(colors, G_PER_VERTEX);
+        line->set_num_prims(1);
+
+        CullableObject *object = 
+          new CullableObject(line, line_state, xform_data._render_transform);
+
+        trav->get_cull_handler()->record_object(object);
+      }
+    }
+
+  }
+
+  // Now carry on to render our child nodes.
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::output
+//       Access: Public, Virtual
+//  Description: Writes a brief description of the node to the
+//               indicated output stream.  This is invoked by the <<
+//               operator.  It may be overridden in derived classes to
+//               include some information relevant to the class.
+////////////////////////////////////////////////////////////////////
+void CollisionVisualizer::
+output(ostream &out) const {
+  PandaNode::output(out);
+  out << " ";
+  CollisionRecorder::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::begin_traversal
+//       Access: Public, Virtual
+//  Description: This method is called at the beginning of a
+//               CollisionTraverser::traverse() call.  It is provided
+//               as a hook for the derived class to reset its state as
+//               appropriate.
+////////////////////////////////////////////////////////////////////
+void CollisionVisualizer::
+begin_traversal() {
+  CollisionRecorder::begin_traversal();
+  _data.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::collision_tested
+//       Access: Public, Virtual
+//  Description: This method is called when a pair of collision solids
+//               have passed all bounding-volume tests and have been
+//               tested for a collision.  The detected value is set
+//               true if a collision was detected, false otherwise.
+////////////////////////////////////////////////////////////////////
+void CollisionVisualizer::
+collision_tested(const CollisionEntry &entry, bool detected) {
+  CollisionRecorder::collision_tested(entry, detected);
+
+  VizInfo &viz_info = _data[entry.get_into_node_path().get_net_transform()];
+  if (detected) {
+    viz_info._solids[entry.get_into()]._detected_count++;
+
+    if (entry.has_into_intersection_point() &&
+        entry.has_into_surface_normal()) {
+      CollisionPoint p;
+      p._point = entry.get_into_intersection_point();
+      p._normal = entry.get_into_surface_normal();
+      viz_info._points.push_back(p);
+    }
+
+  } else {
+    viz_info._solids[entry.get_into()]._missed_count++;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::get_detected_state
+//       Access: Private
+//  Description: Returns a RenderState suitable for rendering the
+//               collision solids with which a collision was detected.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) CollisionVisualizer::
+get_detected_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
+      (DepthOffsetAttrib::make());
+  }
+
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionVisualizer::get_tested_state
+//       Access: Private
+//  Description: Returns a RenderState suitable for rendering the
+//               collision solids with which a collision was tested,
+//               but no collision was detected..
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) CollisionVisualizer::
+get_tested_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
+      (ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 0.5f, 0.5f)),
+       DepthOffsetAttrib::make());
+    state = state->add_attrib
+      (TransparencyAttrib::make(TransparencyAttrib::M_alpha), 1);
+  }
+
+  return state;
+}
+
+#endif  // DO_COLLISION_RECORDING

+ 111 - 0
panda/src/collide/collisionVisualizer.h

@@ -0,0 +1,111 @@
+// Filename: collisionVisualizer.h
+// Created by:  drose (16Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef COLLISIONVISUALIZER_H
+#define COLLISIONVISUALIZER_H
+
+#include "pandabase.h"
+#include "pandaNode.h"
+#include "collisionRecorder.h"
+#include "nodePath.h"
+
+#ifdef DO_COLLISION_RECORDING
+
+////////////////////////////////////////////////////////////////////
+//       Class : CollisionVisualizer
+// Description : This class is used to help debug the work the
+//               collisions system is doing.  It shows the polygons
+//               that are detected as collisions, as well as those
+//               that are simply considered for collisions.
+//
+//               It may be parented anywhere in the scene graph where
+//               it will be rendered to achieve this.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CollisionVisualizer : public PandaNode, public CollisionRecorder {
+PUBLISHED:
+  CollisionVisualizer(const string &name);
+  virtual ~CollisionVisualizer();
+
+  void clear();
+
+public:
+  // from parent class PandaNode.
+  virtual PandaNode *make_copy() const;
+  virtual bool has_cull_callback() const;
+  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual void output(ostream &out) const;
+
+  // from parent class CollisionRecorder.
+  virtual void begin_traversal();
+  virtual void collision_tested(const CollisionEntry &entry, bool detected);
+
+private:
+  CPT(RenderState) get_detected_state();
+  CPT(RenderState) get_tested_state();
+
+private:
+  class SolidInfo {
+  public:
+    INLINE SolidInfo();
+    int _detected_count;
+    int _missed_count;
+  };
+  typedef pmap<CPT(CollisionSolid), SolidInfo> Solids;
+
+  class CollisionPoint {
+  public:
+    LPoint3f _point;
+    LVector3f _normal;
+  };
+  typedef pvector<CollisionPoint> Points;
+
+  class VizInfo {
+  public:
+    Solids _solids;
+    Points _points;
+  };
+
+  typedef map<CPT(TransformState), VizInfo> Data;
+  Data _data;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+
+  static void init_type() {
+    PandaNode::init_type();
+    CollisionRecorder::init_type();
+    register_type(_type_handle, "CollisionVisualizer",
+                  PandaNode::get_class_type(),
+                  CollisionRecorder::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "collisionVisualizer.I"
+
+#endif  // DO_COLLISION_RECORDING  
+
+#endif

+ 10 - 3
panda/src/collide/config_collide.cxx

@@ -28,10 +28,12 @@
 #include "collisionPlane.h"
 #include "collisionPolygon.h"
 #include "collisionRay.h"
+#include "collisionRecorder.h"
 #include "collisionSegment.h"
 #include "collisionSolid.h"
 #include "collisionSphere.h"
-#include <dconfig.h>
+#include "collisionVisualizer.h"
+#include "dconfig.h"
 
 Configure(config_collide);
 NotifyCategoryDef(collide, "");
@@ -76,8 +78,13 @@ init_libcollide() {
   CollisionSolid::init_type();
   CollisionSphere::init_type();
 
-  //Registration of writeable object's creation
-  //functions with BamReader's factory
+#ifdef DO_COLLISION_RECORDING
+  CollisionRecorder::init_type();
+  CollisionVisualizer::init_type();
+#endif
+
+  // Registration of writeable object's creation
+  // functions with BamReader's factory
   CollisionNode::register_with_read_factory();
   CollisionPlane::register_with_read_factory();
   CollisionPolygon::register_with_read_factory();

+ 2 - 2
panda/src/collide/config_collide.h

@@ -19,8 +19,8 @@
 #ifndef CONFIG_COLLIDE_H
 #define CONFIG_COLLIDE_H
 
-#include <pandabase.h>
-#include <notifyCategoryProxy.h>
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
 
 NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA);
 

+ 45 - 22
panda/src/pgraph/cullTraverserData.I

@@ -38,6 +38,37 @@ CullTraverserData(const NodePath &start,
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullTraverserData::
+CullTraverserData(const CullTraverserData &copy) :
+  _node_path(copy._node_path),
+  _render_transform(copy._render_transform),
+  _net_transform(copy._net_transform),
+  _state(copy._state),
+  _view_frustum(copy._view_frustum),
+  _guard_band(copy._guard_band)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void CullTraverserData::
+operator = (const CullTraverserData &copy) {
+  _node_path = copy._node_path;
+  _render_transform = copy._render_transform;
+  _net_transform = copy._net_transform;
+  _state = copy._state;
+  _view_frustum = copy._view_frustum;
+  _guard_band = copy._guard_band;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::Constructor
 //       Access: Public
@@ -55,28 +86,6 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::Copy Constructor
-//       Access: Private
-//  Description: Do not copy CullTraverserData objects.
-////////////////////////////////////////////////////////////////////
-INLINE CullTraverserData::
-CullTraverserData(const CullTraverserData &copy) :
-  _node_path(NodePath())
-{
-  nassertv(false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::Copy Assignment Operator
-//       Access: Private
-//  Description: Do not copy CullTraverserData objects.
-////////////////////////////////////////////////////////////////////
-INLINE void CullTraverserData::
-operator = (const CullTraverserData &copy) {
-  nassertv(false);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::Destructor
 //       Access: Public
@@ -127,3 +136,17 @@ is_in_view(const DrawMask &camera_mask) {
   // Otherwise, compare the bounding volume to the frustum.
   return is_in_view_impl();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::apply_transform_and_state
+//       Access: Public
+//  Description: Applies the transform and state from the current
+//               node onto the current data.  This also evaluates
+//               billboards, etc.
+////////////////////////////////////////////////////////////////////
+INLINE void CullTraverserData::
+apply_transform_and_state(CullTraverser *trav) {
+  apply_transform_and_state(trav, node()->get_transform(),
+                            node()->get_state(), node()->get_effects());
+}
+

+ 8 - 11
panda/src/pgraph/cullTraverserData.cxx

@@ -27,22 +27,21 @@
 #include "compassEffect.h"
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::apply_transform_and_state
+//     Function: CullTraverserData::apply_specific_transform
 //       Access: Public
-//  Description: Applies the transform and state from the current
-//               node onto the current data.  This also evaluates
-//               billboards, etc.
+//  Description: Applies the indicated transform and state changes
+//               (e.g. as extracted from a node) onto the current
+//               data.  This also evaluates billboards, etc.
 ////////////////////////////////////////////////////////////////////
 void CullTraverserData::
-apply_transform_and_state(CullTraverser *trav) {
-  CPT(TransformState) node_transform = node()->get_transform();
-
+apply_transform_and_state(CullTraverser *trav, 
+                          CPT(TransformState) node_transform, 
+                          CPT(RenderState) node_state,
+                          CPT(RenderEffects) node_effects) {
   // First, compute the _net_transform, because we need it for the
   // compass and billboard effects.
   _net_transform = _net_transform->compose(node_transform);
 
-  const RenderEffects *node_effects = node()->get_effects();
-
   const CompassEffect *compass = node_effects->get_compass();
   if (compass != (const CompassEffect *)NULL) {
     CPT(TransformState) compass_transform = 
@@ -92,11 +91,9 @@ apply_transform_and_state(CullTraverser *trav) {
     }
   }
 
-  const RenderState *node_state = node()->get_state();
   _state = _state->compose(node_state);
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::is_in_view_impl
 //       Access: Private

+ 7 - 7
panda/src/pgraph/cullTraverserData.h

@@ -53,20 +53,20 @@ public:
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
                            GeometricBoundingVolume *guard_band);
-  INLINE CullTraverserData(const CullTraverserData &parent, 
-                           PandaNode *child);
-
-private:
   INLINE CullTraverserData(const CullTraverserData &copy);
   INLINE void operator = (const CullTraverserData &copy); 
-
-public:
+  INLINE CullTraverserData(const CullTraverserData &parent, 
+                           PandaNode *child);
   INLINE ~CullTraverserData();
 
   INLINE PandaNode *node() const;
 
   INLINE bool is_in_view(const DrawMask &camera_mask);
-  void apply_transform_and_state(CullTraverser *trav);
+  INLINE void apply_transform_and_state(CullTraverser *trav);
+  void apply_transform_and_state(CullTraverser *trav, 
+                                 CPT(TransformState) node_transform, 
+                                 CPT(RenderState) node_state,
+                                 CPT(RenderEffects) node_effects);
 
   WorkingNodePath _node_path;
   CPT(TransformState) _render_transform;