Browse Source

add CollisionSolid::set_effective_normal

David Rose 22 years ago
parent
commit
2536db25f4

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

@@ -54,8 +54,7 @@ make_copy() {
 void CollisionPlane::
 void CollisionPlane::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   _plane = _plane * mat;
   _plane = _plane * mat;
-  mark_viz_stale();
-  mark_bound_stale();
+  CollisionSolid::xform(mat);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -130,7 +129,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
 
 
   LVector3f from_normal = get_normal() * entry.get_inv_wrt_mat();
   LVector3f from_normal = get_normal() * entry.get_inv_wrt_mat();
 
 
-  new_entry->set_surface_normal(get_normal());
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : get_normal());
   new_entry->set_surface_point(from_center - get_normal() * dist);
   new_entry->set_surface_point(from_center - get_normal() * dist);
   new_entry->set_interior_point(from_center - get_normal() * from_radius);
   new_entry->set_interior_point(from_center - get_normal() * from_radius);
 
 
@@ -171,7 +170,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
 
   LPoint3f into_intersection_point = from_origin + t * from_direction;
   LPoint3f into_intersection_point = from_origin + t * from_direction;
-  new_entry->set_surface_normal(get_normal());
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : get_normal());
   new_entry->set_surface_point(into_intersection_point);
   new_entry->set_surface_point(into_intersection_point);
 
 
   return new_entry;
   return new_entry;

+ 4 - 5
panda/src/collide/collisionPolygon.cxx

@@ -228,8 +228,7 @@ xform(const LMatrix4f &mat) {
     setup_points(verts_begin, verts_end);
     setup_points(verts_begin, verts_end);
   }
   }
 
 
-  mark_viz_stale();
-  mark_bound_stale();
+  CollisionSolid::xform(mat);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -513,7 +512,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
     into_depth = from_radius - dist_to_plane(orig_center);
     into_depth = from_radius - dist_to_plane(orig_center);
   }
   }
 
 
-  new_entry->set_surface_normal(get_normal());
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : get_normal());
   new_entry->set_surface_point(from_center - get_normal() * dist);
   new_entry->set_surface_point(from_center - get_normal() * dist);
   new_entry->set_interior_point(from_center - get_normal() * (dist + into_depth));
   new_entry->set_interior_point(from_center - get_normal() * (dist + into_depth));
 
 
@@ -581,7 +580,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
   }
   }
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
 
-  new_entry->set_surface_normal(get_normal());
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : get_normal());
   new_entry->set_surface_point(plane_point);
   new_entry->set_surface_point(plane_point);
 
 
   return new_entry;
   return new_entry;
@@ -650,7 +649,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
   }
   }
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
 
-  new_entry->set_surface_normal(get_normal());
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : get_normal());
   new_entry->set_surface_point(plane_point);
   new_entry->set_surface_point(plane_point);
 
 
   return new_entry;
   return new_entry;

+ 2 - 2
panda/src/collide/collisionRay.cxx

@@ -63,8 +63,8 @@ void CollisionRay::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   _origin = _origin * mat;
   _origin = _origin * mat;
   _direction = _direction * mat;
   _direction = _direction * mat;
-  mark_viz_stale();
-  mark_bound_stale();
+
+  CollisionSolid::xform(mat);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/collide/collisionSegment.cxx

@@ -65,8 +65,8 @@ void CollisionSegment::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   _a = _a * mat;
   _a = _a * mat;
   _b = _b * mat;
   _b = _b * mat;
-  mark_viz_stale();
-  mark_bound_stale();
+
+  CollisionSolid::xform(mat);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 62 - 7
panda/src/collide/collisionSolid.I

@@ -19,7 +19,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::set_tangible
 //     Function: CollisionSolid::set_tangible
-//       Access: Public
+//       Access: Published
 //  Description: Sets the current state of the 'tangible' flag.  Set
 //  Description: Sets the current state of the 'tangible' flag.  Set
 //               this true to make the solid tangible, so that a
 //               this true to make the solid tangible, so that a
 //               CollisionHandlerPusher will not allow another object
 //               CollisionHandlerPusher will not allow another object
@@ -29,15 +29,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void CollisionSolid::
 INLINE void CollisionSolid::
 set_tangible(bool tangible) {
 set_tangible(bool tangible) {
-  if (tangible != _tangible) {
-    _tangible = tangible;
-    mark_viz_stale();
+  if (tangible) {
+    _flags |= F_tangible;
+  } else {
+    _flags &= ~F_tangible;
   }
   }
+  mark_viz_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::is_tangible
 //     Function: CollisionSolid::is_tangible
-//       Access: Public
+//       Access: Published
 //  Description: Returns whether the solid is considered 'tangible' or
 //  Description: Returns whether the solid is considered 'tangible' or
 //               not.  An intangible solid has no effect in a
 //               not.  An intangible solid has no effect in a
 //               CollisionHandlerPusher (except to throw an event);
 //               CollisionHandlerPusher (except to throw an event);
@@ -46,7 +48,60 @@ set_tangible(bool tangible) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool CollisionSolid::
 INLINE bool CollisionSolid::
 is_tangible() const {
 is_tangible() const {
-  return _tangible;
+  return (_flags & F_tangible) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::set_effective_normal
+//       Access: Published
+//  Description: Records a false normal for this CollisionSolid that
+//               will be reported by the collision system with all
+//               collisions into it, instead of its actual normal.
+//               This is useful as a workaround for the problem of an
+//               avatar wanting to stand on a sloping ground; by
+//               storing a false normal, the ground appears to be
+//               perfectly level, and the avatar does not tend to
+//               slide down it.
+////////////////////////////////////////////////////////////////////
+INLINE void CollisionSolid::
+set_effective_normal(const LVector3f &effective_normal) {
+  _effective_normal = effective_normal;
+  _flags |= F_effective_normal;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::clear_effective_normal
+//       Access: Published
+//  Description: Removes the normal previously set by
+//               set_effective_normal().
+////////////////////////////////////////////////////////////////////
+INLINE void CollisionSolid::
+clear_effective_normal() {
+  _flags &= ~F_effective_normal;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::has_effective_normal
+//       Access: Published
+//  Description: Returns true if a special normal was set by
+//               set_effective_normal(), false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool CollisionSolid::
+has_effective_normal() const {
+  return (_flags & F_effective_normal) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::get_effective_normal
+//       Access: Published
+//  Description: Returns the normal that was set by
+//               set_effective_normal().  It is an error to call this
+//               unless has_effective_normal() returns true.
+////////////////////////////////////////////////////////////////////
+INLINE const LVector3f &CollisionSolid::
+get_effective_normal() const {
+  nassertr(has_effective_normal(), LVector3f::zero());
+  return _effective_normal;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -59,5 +114,5 @@ is_tangible() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void CollisionSolid::
 INLINE void CollisionSolid::
 mark_viz_stale() {
 mark_viz_stale() {
-  _viz_geom_stale = true;
+  _flags |= F_viz_geom_stale;
 }
 }

+ 51 - 24
panda/src/collide/collisionSolid.cxx

@@ -16,7 +16,6 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-
 #include "collisionSolid.h"
 #include "collisionSolid.h"
 #include "config_collide.h"
 #include "config_collide.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
@@ -43,8 +42,7 @@ TypeHandle CollisionSolid::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CollisionSolid::
 CollisionSolid::
 CollisionSolid() {
 CollisionSolid() {
-  _viz_geom_stale = true;
-  _tangible = true;
+  _flags = F_viz_geom_stale | F_tangible;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -54,10 +52,10 @@ CollisionSolid() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CollisionSolid::
 CollisionSolid::
 CollisionSolid(const CollisionSolid &copy) :
 CollisionSolid(const CollisionSolid &copy) :
-  _tangible(copy._tangible)
+  _effective_normal(copy._effective_normal),
+  _flags(copy._flags)
 {
 {
-  // Actually, there's not a whole lot here we want to copy.
-  _viz_geom_stale = true;
+  _flags &= ~F_viz_geom_stale;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -84,6 +82,21 @@ test_intersection(const CollisionEntry &) const {
   return NULL;
   return NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::xform
+//       Access: Public, Virtual
+//  Description: Transforms the solid by the indicated matrix.
+////////////////////////////////////////////////////////////////////
+void CollisionSolid::
+xform(const LMatrix4f &mat) {
+  if (has_effective_normal()) {
+    _effective_normal = _effective_normal * mat;
+  }
+
+  mark_viz_stale();
+  mark_bound_stale();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::get_viz
 //     Function: CollisionSolid::get_viz
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -94,14 +107,14 @@ test_intersection(const CollisionEntry &) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(PandaNode) CollisionSolid::
 PT(PandaNode) CollisionSolid::
 get_viz(const CullTraverserData &) const {
 get_viz(const CullTraverserData &) const {
-  if (_viz_geom_stale) {
+  if ((_flags & F_viz_geom_stale) != 0) {
     if (_viz_geom == (GeomNode *)NULL) {
     if (_viz_geom == (GeomNode *)NULL) {
       ((CollisionSolid *)this)->_viz_geom = new GeomNode("viz");
       ((CollisionSolid *)this)->_viz_geom = new GeomNode("viz");
     } else {
     } else {
       _viz_geom->remove_all_geoms();
       _viz_geom->remove_all_geoms();
     }
     }
     ((CollisionSolid *)this)->fill_viz_geom();
     ((CollisionSolid *)this)->fill_viz_geom();
-    ((CollisionSolid *)this)->_viz_geom_stale = false;
+    ((CollisionSolid *)this)->_flags &= ~F_viz_geom_stale;
   }
   }
   return _viz_geom.p();
   return _viz_geom.p();
 }
 }
@@ -246,9 +259,13 @@ report_undefined_from_intersection(TypeHandle from_type) {
 //               the particular object to a Datagram
 //               the particular object to a Datagram
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionSolid::
 void CollisionSolid::
-write_datagram(BamWriter *, Datagram &me)
-{
-  me.add_uint8(_tangible);
+write_datagram(BamWriter *, Datagram &me) {
+  // For now, we need only 8 bits of flags.  If we need to expand this
+  // later, we will have to increase the bam version.
+  me.add_uint8(_flags);
+  if ((_flags & F_effective_normal) != 0) {
+    _effective_normal.write_datagram(me);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -260,9 +277,11 @@ write_datagram(BamWriter *, Datagram &me)
 //               place
 //               place
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionSolid::
 void CollisionSolid::
-fillin(DatagramIterator& scan, BamReader*)
-{
-  _tangible = (scan.get_uint8() != 0);
+fillin(DatagramIterator &scan, BamReader*) {
+  _flags = scan.get_uint8();
+  if ((_flags & F_effective_normal) != 0) {
+    _effective_normal.read_datagram(scan);
+  }
 }
 }
 
 
 
 
@@ -296,21 +315,29 @@ get_solid_viz_state() {
        TransparencyAttrib::make(TransparencyAttrib::M_alpha));
        TransparencyAttrib::make(TransparencyAttrib::M_alpha));
   }
   }
 
 
-  if (_tangible) {
-    static CPT(RenderState) tangible_state = (const RenderState *)NULL;
-    if (tangible_state == (const RenderState *)NULL) {
-      tangible_state = base_state->add_attrib
-        (ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 0.5f)));
-    }
-    return tangible_state;
-
-  } else {
+  if (!is_tangible()) {
     static CPT(RenderState) intangible_state = (const RenderState *)NULL;
     static CPT(RenderState) intangible_state = (const RenderState *)NULL;
     if (intangible_state == (const RenderState *)NULL) {
     if (intangible_state == (const RenderState *)NULL) {
       intangible_state = base_state->add_attrib
       intangible_state = base_state->add_attrib
         (ColorAttrib::make_flat(Colorf(1.0f, 0.3f, 0.5f, 0.5f)));
         (ColorAttrib::make_flat(Colorf(1.0f, 0.3f, 0.5f, 0.5f)));
     }
     }
     return intangible_state;
     return intangible_state;
+
+  } else if (has_effective_normal()) {
+    static CPT(RenderState) fakenormal_state = (const RenderState *)NULL;
+    if (fakenormal_state == (const RenderState *)NULL) {
+      fakenormal_state = base_state->add_attrib
+        (ColorAttrib::make_flat(Colorf(0.5f, 0.5f, 1.0f, 0.5f)));
+    }
+    return fakenormal_state;
+
+  } else {
+    static CPT(RenderState) tangible_state = (const RenderState *)NULL;
+    if (tangible_state == (const RenderState *)NULL) {
+      tangible_state = base_state->add_attrib
+        (ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 0.5f)));
+    }
+    return tangible_state;
   }
   }
 }
 }
 
 
@@ -335,7 +362,7 @@ get_wireframe_viz_state() {
        TransparencyAttrib::make(TransparencyAttrib::M_none));
        TransparencyAttrib::make(TransparencyAttrib::M_none));
   }
   }
 
 
-  if (_tangible) {
+  if (is_tangible()) {
     static CPT(RenderState) tangible_state = (const RenderState *)NULL;
     static CPT(RenderState) tangible_state = (const RenderState *)NULL;
     if (tangible_state == (const RenderState *)NULL) {
     if (tangible_state == (const RenderState *)NULL) {
       tangible_state = base_state->add_attrib
       tangible_state = base_state->add_attrib

+ 16 - 3
panda/src/collide/collisionSolid.h

@@ -62,11 +62,16 @@ PUBLISHED:
   INLINE void set_tangible(bool tangible);
   INLINE void set_tangible(bool tangible);
   INLINE bool is_tangible() const;
   INLINE bool is_tangible() const;
 
 
+  INLINE void set_effective_normal(const LVector3f &effective_normal);
+  INLINE void clear_effective_normal();
+  INLINE bool has_effective_normal() const;
+  INLINE const LVector3f &get_effective_normal() const;
+
 public:
 public:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection(const CollisionEntry &entry) const;
   test_intersection(const CollisionEntry &entry) const;
 
 
-  virtual void xform(const LMatrix4f &mat)=0;
+  virtual void xform(const LMatrix4f &mat);
 
 
   virtual PT(PandaNode) get_viz(const CullTraverserData &data) const;
   virtual PT(PandaNode) get_viz(const CullTraverserData &data) const;
 
 
@@ -94,8 +99,16 @@ protected:
   CPT(RenderState) get_other_viz_state();
   CPT(RenderState) get_other_viz_state();
 
 
   PT(GeomNode) _viz_geom;
   PT(GeomNode) _viz_geom;
-  bool _viz_geom_stale;
-  bool _tangible;
+
+private:
+  LVector3f _effective_normal;
+
+  enum Flags {
+    F_tangible         = 0x01,
+    F_effective_normal = 0x02,
+    F_viz_geom_stale   = 0x04,
+  };
+  int _flags;
 
 
 public:
 public:
   virtual void write_datagram(BamWriter* manager, Datagram &me);
   virtual void write_datagram(BamWriter* manager, Datagram &me);

+ 1 - 2
panda/src/collide/collisionSphere.cxx

@@ -67,7 +67,6 @@ xform(const LMatrix4f &mat) {
   // non-uniform scale.
   // non-uniform scale.
   LVector3f radius_v = LVector3f(_radius, 0.0f, 0.0f) * mat;
   LVector3f radius_v = LVector3f(_radius, 0.0f, 0.0f) * mat;
   _radius = length(radius_v);
   _radius = length(radius_v);
-
   mark_viz_stale();
   mark_viz_stale();
   mark_bound_stale();
   mark_bound_stale();
 }
 }
@@ -156,7 +155,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
     surface_normal = vec / vec_length;
     surface_normal = vec / vec_length;
   }
   }
 
 
-  new_entry->set_surface_normal(surface_normal);
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : surface_normal);
   new_entry->set_surface_point(into_center + surface_normal * into_radius);
   new_entry->set_surface_point(into_center + surface_normal * into_radius);
   new_entry->set_interior_point(from_center - surface_normal * from_radius);
   new_entry->set_interior_point(from_center - surface_normal * from_radius);
 
 

+ 2 - 4
panda/src/collide/collisionTube.cxx

@@ -62,6 +62,7 @@ xform(const LMatrix4f &mat) {
   _radius = length(radius_v);
   _radius = length(radius_v);
 
 
   recalc_internals();
   recalc_internals();
+  CollisionSolid::xform(mat);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -367,9 +368,6 @@ recalc_internals() {
   look_at(_mat, direction, LVector3f(0.0f, 0.0f, 1.0f), CS_zup_right);
   look_at(_mat, direction, LVector3f(0.0f, 0.0f, 1.0f), CS_zup_right);
   _mat.set_row(3, _a);
   _mat.set_row(3, _a);
   _inv_mat.invert_from(_mat);
   _inv_mat.invert_from(_mat);
-
-  mark_viz_stale();
-  mark_bound_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -693,8 +691,8 @@ set_intersection_point(CollisionEntry *new_entry,
   // our collision was tangential.
   // our collision was tangential.
   LPoint3f orig_point = into_intersection_point - normal * extra_radius;
   LPoint3f orig_point = into_intersection_point - normal * extra_radius;
 
 
+  new_entry->set_surface_normal(has_effective_normal() ? get_effective_normal() : normal);
   new_entry->set_surface_point(point);
   new_entry->set_surface_point(point);
-  new_entry->set_surface_normal(normal);
   new_entry->set_interior_point(orig_point);
   new_entry->set_interior_point(orig_point);
 }
 }
 
 

+ 11 - 10
panda/src/collide/collisionVisualizer.cxx

@@ -146,19 +146,20 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
       const CollisionSolid *solid = (*si).first;
       const CollisionSolid *solid = (*si).first;
       const SolidInfo &solid_info = (*si).second;
       const SolidInfo &solid_info = (*si).second;
       PT(PandaNode) node = solid->get_viz(xform_data);
       PT(PandaNode) node = solid->get_viz(xform_data);
+      if (node != (PandaNode *)NULL) {
+        CullTraverserData next_data(xform_data, node);
         
         
-      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();
+        }
         
         
-      // 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);
       }
       }
-        
-      trav->traverse(next_data);
     }
     }
 
 
     // Now draw all of the detected points.
     // Now draw all of the detected points.

+ 7 - 13
panda/src/doc/eggSyntax.txt

@@ -971,19 +971,6 @@ GROUPING ENTRIES
       The geometry represents a sphere.  The vertices in the group are
       The geometry represents a sphere.  The vertices in the group are
       averaged together to determine the sphere's center and radius.
       averaged together to determine the sphere's center and radius.
 
 
-    InverseSphere
-
-      The geometry represents a sphere with the normal inverted, to
-      make a hollow spherical shell.
-
-    Geode
-
-      The geometry is not a collision surface after all, but is in
-      fact true geometry.  This is intended to be used in conjunction
-      with one or more flags to define collision properties for normal
-      geometry.
-
-
 
 
     The flags may be any zero or more of:
     The flags may be any zero or more of:
 
 
@@ -1038,6 +1025,13 @@ GROUPING ENTRIES
       can go forwards but not back.  Presently, this is only
       can go forwards but not back.  Presently, this is only
       implemented for planes, polygons, and polysets.
       implemented for planes, polygons, and polysets.
 
 
+    level
+
+      Stores a special effective normal with the collision solid that
+      points up, regardless of the actual shape or orientation of the
+      solid.  This can be used to allow an avatar to stand on a
+      sloping surface without having a tendency to slide downward.
+
 
 
   <ObjectType> { type }
   <ObjectType> { type }
 
 

+ 6 - 8
panda/src/egg/eggGroup.cxx

@@ -781,10 +781,6 @@ string_cs_type(const string &string) {
     return CST_polyset;
     return CST_polyset;
   } else if (cmp_nocase_uh(string, "sphere") == 0) {
   } else if (cmp_nocase_uh(string, "sphere") == 0) {
     return CST_sphere;
     return CST_sphere;
-  } else if (cmp_nocase_uh(string, "inversesphere") == 0) {
-    return CST_inverse_sphere;
-  } else if (cmp_nocase_uh(string, "geode") == 0) {
-    return CST_geode;
   } else if (cmp_nocase_uh(string, "tube") == 0) {
   } else if (cmp_nocase_uh(string, "tube") == 0) {
     return CST_tube;
     return CST_tube;
   } else {
   } else {
@@ -817,6 +813,8 @@ string_collide_flags(const string &string) {
     return CF_center;
     return CF_center;
   } else if (cmp_nocase_uh(string, "turnstile") == 0) {
   } else if (cmp_nocase_uh(string, "turnstile") == 0) {
     return CF_turnstile;
     return CF_turnstile;
+  } else if (cmp_nocase_uh(string, "level") == 0) {
+    return CF_level;
   } else {
   } else {
     return CF_none;
     return CF_none;
   }
   }
@@ -1155,10 +1153,6 @@ ostream &operator << (ostream &out, EggGroup::CollisionSolidType t) {
     return out << "Polyset";
     return out << "Polyset";
   case EggGroup::CST_sphere:
   case EggGroup::CST_sphere:
     return out << "Sphere";
     return out << "Sphere";
-  case EggGroup::CST_inverse_sphere:
-    return out << "InverseSphere";
-  case EggGroup::CST_geode:
-    return out << "Geode";
   case EggGroup::CST_tube:
   case EggGroup::CST_tube:
     return out << "Tube";
     return out << "Tube";
   }
   }
@@ -1206,5 +1200,9 @@ ostream &operator << (ostream &out, EggGroup::CollideFlags t) {
     out << space << "turnstile";
     out << space << "turnstile";
     space = " ";
     space = " ";
   }
   }
+  if (bits & EggGroup::CF_level) {
+    out << space << "level";
+    space = " ";
+  }
   return out;
   return out;
 }
 }

+ 3 - 4
panda/src/egg/eggGroup.h

@@ -78,9 +78,7 @@ PUBLISHED:
     CST_polygon              = 0x00020000,
     CST_polygon              = 0x00020000,
     CST_polyset              = 0x00030000,
     CST_polyset              = 0x00030000,
     CST_sphere               = 0x00040000,
     CST_sphere               = 0x00040000,
-    CST_inverse_sphere       = 0x00050000,
-    CST_geode                = 0x00060000,
-    CST_tube                 = 0x00070000,
+    CST_tube                 = 0x00050000,
   };
   };
   enum CollideFlags {
   enum CollideFlags {
     // The bits here must correspond to those in Flags, below.
     // The bits here must correspond to those in Flags, below.
@@ -92,6 +90,7 @@ PUBLISHED:
     CF_solid                 = 0x00800000,
     CF_solid                 = 0x00800000,
     CF_center                = 0x01000000,
     CF_center                = 0x01000000,
     CF_turnstile             = 0x02000000,
     CF_turnstile             = 0x02000000,
+    CF_level                 = 0x04000000,
   };
   };
 
 
   EggGroup(const string &name = "");
   EggGroup(const string &name = "");
@@ -256,7 +255,7 @@ private:
     F_decal_flag             = 0x00002000,
     F_decal_flag             = 0x00002000,
     F_direct_flag            = 0x00004000,
     F_direct_flag            = 0x00004000,
     F_cs_type                = 0x00070000,
     F_cs_type                = 0x00070000,
-    F_collide_flags          = 0x03f80000,
+    F_collide_flags          = 0x07f80000,
   };
   };
   enum Flags2 {
   enum Flags2 {
     F2_collide_mask          = 0x00000001,
     F2_collide_mask          = 0x00000001,

+ 4 - 11
panda/src/egg2pg/eggLoader.cxx

@@ -1555,8 +1555,7 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
     CharacterMaker char_maker(egg_group, *this);
     CharacterMaker char_maker(egg_group, *this);
     node = char_maker.make_node();
     node = char_maker.make_node();
 
 
-  } else if (egg_group->get_cs_type() != EggGroup::CST_none &&
-             egg_group->get_cs_type() != EggGroup::CST_geode) {
+  } else if (egg_group->get_cs_type() != EggGroup::CST_none) {
     // A collision group: create collision geometry.
     // A collision group: create collision geometry.
     node = new CollisionNode(egg_group->get_name());
     node = new CollisionNode(egg_group->get_name());
 
 
@@ -1771,18 +1770,9 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
 
 
   switch (start_group->get_cs_type()) {
   switch (start_group->get_cs_type()) {
   case EggGroup::CST_none:
   case EggGroup::CST_none:
-  case EggGroup::CST_geode:
     // No collision flags; do nothing.  Don't even traverse further.
     // No collision flags; do nothing.  Don't even traverse further.
     return;
     return;
 
 
-  case EggGroup::CST_inverse_sphere:
-    // These aren't presently supported.
-    egg2pg_cat.error()
-      << "Not presently supported: <Collide> { "
-      << egg_group->get_cs_type() << " }\n";
-    _error = true;
-    break;
-
   case EggGroup::CST_plane:
   case EggGroup::CST_plane:
     make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
     make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
     break;
     break;
@@ -2159,6 +2149,9 @@ apply_collision_flags(CollisionSolid *solid,
   if ((flags & EggGroup::CF_intangible) != 0) {
   if ((flags & EggGroup::CF_intangible) != 0) {
     solid->set_tangible(false);
     solid->set_tangible(false);
   }
   }
+  if ((flags & EggGroup::CF_level) != 0) {
+    solid->set_effective_normal(LVector3f::up());
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -34,13 +34,14 @@ 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 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.
 // Bumped to major version 4 on 4/10/02 to store new scene graph.
 
 
-static const unsigned short _bam_minor_ver = 6;
+static const unsigned short _bam_minor_ver = 7;
 // 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.
 // Bumped to minor version 2 on 4/12/03 to add num_components to texture.
 // Bumped to minor version 3 on 4/15/03 to add ImageBuffer::_alpha_file_channel
 // Bumped to minor version 3 on 4/15/03 to add ImageBuffer::_alpha_file_channel
 // Bumped to minor version 4 on 6/12/03 to add PandaNode::set_tag().
 // Bumped to minor version 4 on 6/12/03 to add PandaNode::set_tag().
 // Bumped to minor version 5 on 7/09/03 to add rawdata mode to texture.
 // Bumped to minor version 5 on 7/09/03 to add rawdata mode to texture.
 // Bumped to minor version 6 on 7/22/03 to add shear to scene graph and animation data.
 // Bumped to minor version 6 on 7/22/03 to add shear to scene graph and animation data.
+// Bumped to minor version 7 on 11/10/03 to add CollisionSolid::_effective_normal
 
 
 
 
 #endif
 #endif