diff --git a/panda/src/collide/Sources.pp b/panda/src/collide/Sources.pp index a40980fb50..2d830ad648 100644 --- a/panda/src/collide/Sources.pp +++ b/panda/src/collide/Sources.pp @@ -20,7 +20,9 @@ collisionNode.cxx collisionNode.h collisionPlane.I \ collisionPlane.cxx collisionPlane.h collisionPolygon.I \ collisionPolygon.cxx collisionPolygon.h collisionRay.I \ - collisionRay.cxx collisionRay.h collisionSolid.I collisionSolid.cxx \ + collisionRay.cxx collisionRay.h collisionSegment.I \ + collisionSegment.cxx collisionSegment.h \ + collisionSolid.I collisionSolid.cxx \ collisionSolid.h collisionSphere.I collisionSphere.cxx \ collisionSphere.h collisionTraverser.I collisionTraverser.cxx \ collisionTraverser.h config_collide.cxx config_collide.h @@ -34,6 +36,7 @@ collisionHandlerQueue.h collisionLevelState.I collisionLevelState.h \ collisionNode.I collisionNode.h collisionPlane.I collisionPlane.h \ collisionPolygon.I collisionPolygon.h collisionRay.I collisionRay.h \ + collisionSegment.I collisionSegment.h \ collisionSolid.I collisionSolid.h collisionSphere.I \ collisionSphere.h collisionTraverser.I collisionTraverser.h diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx index d442b737d8..3136a5fa8a 100644 --- a/panda/src/collide/collisionPolygon.cxx +++ b/panda/src/collide/collisionPolygon.cxx @@ -8,6 +8,7 @@ #include "collisionEntry.h" #include "collisionSphere.h" #include "collisionRay.h" +#include "collisionSegment.h" #include "config_collide.h" #include @@ -342,6 +343,56 @@ test_intersection_from_ray(CollisionHandler *record, return 1; } +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::test_intersection_from_segment +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPolygon:: +test_intersection_from_segment(CollisionHandler *record, + const CollisionEntry &entry) const { + if (_points.size() < 3) { + return 0; + } + + const CollisionSegment *segment; + DCAST_INTO_R(segment, entry.get_from(), 0); + + LPoint3f from_a = segment->get_point_a() * entry.get_wrt_space(); + LPoint3f from_b = segment->get_point_b() * entry.get_wrt_space(); + LPoint3f from_direction = from_b - from_a; + + float t; + if (!get_plane().intersects_line(t, from_a, from_direction)) { + // No intersection. + return 0; + } + + if (t < 0.0 || t > 1.0) { + // The intersection point is before the start of the segment or + // after the end of the segment. + return 0; + } + + LPoint3f plane_point = from_a + t * from_direction; + if (!is_inside(to_2d(plane_point))) { + // Outside the polygon's perimeter. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + new_entry->set_into_intersection_point(plane_point); + + record->add_entry(new_entry); + return 1; +} + //////////////////////////////////////////////////////////////////// // Function: CollisionPolygon::recompute_viz // Access: Public, Virtual diff --git a/panda/src/collide/collisionPolygon.h b/panda/src/collide/collisionPolygon.h index a29f171fc6..1979939235 100644 --- a/panda/src/collide/collisionPolygon.h +++ b/panda/src/collide/collisionPolygon.h @@ -56,6 +56,9 @@ protected: 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 void recompute_viz(Node *parent); diff --git a/panda/src/collide/collisionSegment.I b/panda/src/collide/collisionSegment.I new file mode 100644 index 0000000000..fe87b32e9f --- /dev/null +++ b/panda/src/collide/collisionSegment.I @@ -0,0 +1,119 @@ +// Filename: collisionSegment.I +// Created by: drose (30Jan01) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::Default Constructor +// Access: Public +// Description: Creates an invalid segment. This isn't terribly useful; +// it's expected that the user will subsequently adjust +// the segment via set_origin()/set_direction() or +// set_projection(). +//////////////////////////////////////////////////////////////////// +INLINE CollisionSegment:: +CollisionSegment() : + _a(LPoint3f(0.0, 0.0, 0.0)), + _b(LPoint3f(0.0, 0.0, 0.0)) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSegment:: +CollisionSegment(const LPoint3f &a, const LPoint3f &b) : + _a(a), _b(b) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSegment:: +CollisionSegment(float ax, float ay, float az, + float bx, float by, float bz) : + _a(ax, ay, az), _b(bx, by, bz) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSegment:: +CollisionSegment(const CollisionSegment ©) : + CollisionSolid(copy), + _a(copy._a), + _b(copy._b) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::set_point_a +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSegment:: +set_point_a(const LPoint3f &a) { + _a = a; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::set_point_a +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSegment:: +set_point_a(float x, float y, float z) { + set_point_a(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::get_point_a +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &CollisionSegment:: +get_point_a() const { + return _a; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::set_point_b +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSegment:: +set_point_b(const LPoint3f &b) { + _b = b; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::set_point_b +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSegment:: +set_point_b(float x, float y, float z) { + set_point_b(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::get_point_b +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &CollisionSegment:: +get_point_b() const { + return _b; +} diff --git a/panda/src/collide/collisionSegment.cxx b/panda/src/collide/collisionSegment.cxx new file mode 100644 index 0000000000..6caa26fd60 --- /dev/null +++ b/panda/src/collide/collisionSegment.cxx @@ -0,0 +1,111 @@ +// Filename: collisionSegment.cxx +// Created by: drose (30Jan01) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionSegment.h" +#include "collisionHandler.h" +#include "collisionEntry.h" +#include "config_collide.h" + +#include +#include +#include +#include + +TypeHandle CollisionSegment::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid *CollisionSegment:: +make_copy() { + return new CollisionSegment(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::test_intersection +// 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); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::xform +// Access: Public, Virtual +// Description: Transforms the solid by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void CollisionSegment:: +xform(const LMatrix4f &mat) { + _a = _a * mat; + _b = _b * mat; + clear_viz_arcs(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSegment:: +output(ostream &out) const { + out << "segment, a (" << _a << "), b (" << _b << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::recompute_bound +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSegment:: +recompute_bound() { + BoundedObject::recompute_bound(); + + if (_bound->is_of_type(GeometricBoundingVolume::get_class_type())) { + GeometricBoundingVolume *gbound; + DCAST_INTO_V(gbound, _bound); + + // This makes the assumption that _a and _b are laid out + // sequentially in memory. It works because that's they way + // they're defined in the class. + nassertv(&_a + 1 == &_b); + gbound->around(&_a, &_b + 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSegment::recompute_viz +// Access: Public, Virtual +// Description: Rebuilds the geometry that will be used to render a +// visible representation of the collision solid. +//////////////////////////////////////////////////////////////////// +void CollisionSegment:: +recompute_viz(Node *parent) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Recomputing viz for " << *this << " on " << *parent << "\n"; + } + + GeomLine *segment = new GeomLine; + PTA_Vertexf verts; + PTA_Colorf colors; + verts.push_back(_a); + verts.push_back(_b); + colors.push_back(Colorf(1.0, 1.0, 1.0, 1.0)); + segment->set_coords(verts, G_PER_VERTEX); + segment->set_colors(colors, G_OVERALL); + + segment->set_num_prims(1); + + GeomNode *viz = new GeomNode("viz-segment"); + viz->add_geom(segment); + add_other_viz(parent, viz); +} diff --git a/panda/src/collide/collisionSegment.h b/panda/src/collide/collisionSegment.h new file mode 100644 index 0000000000..d776659f7c --- /dev/null +++ b/panda/src/collide/collisionSegment.h @@ -0,0 +1,81 @@ +// Filename: collisionSegment.h +// Created by: drose (30Jan01) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONSEGMENT_H +#define COLLISIONSEGMENT_H + +#include + +#include "collisionSolid.h" + +class ProjectionNode; + +/////////////////////////////////////////////////////////////////// +// Class : CollisionSegment +// Description : A finite line segment, with two specific endpoints +// but no thickness. It's similar to a CollisionRay, +// except it does not continue to infinity. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionSegment : public CollisionSolid { +PUBLISHED: + INLINE CollisionSegment(); + INLINE CollisionSegment(const LPoint3f &a, const LPoint3f &db); + INLINE CollisionSegment(float ax, float ay, float az, + float bx, float by, float bz); + +public: + INLINE CollisionSegment(const CollisionSegment ©); + virtual CollisionSolid *make_copy(); + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const; + + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + +PUBLISHED: + INLINE void set_point_a(const LPoint3f &a); + INLINE void set_point_a(float x, float y, float z); + INLINE const LPoint3f &get_point_a() const; + + INLINE void set_point_b(const LPoint3f &b); + INLINE void set_point_b(float x, float y, float z); + INLINE const LPoint3f &get_point_b() const; + +protected: + virtual void recompute_bound(); + +protected: + virtual void recompute_viz(Node *parent); + +private: + LPoint3f _a, _b; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionSolid::init_type(); + register_type(_type_handle, "CollisionSegment", + CollisionSolid::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 "collisionSegment.I" + +#endif + + diff --git a/panda/src/collide/collisionSolid.cxx b/panda/src/collide/collisionSolid.cxx index 76a18e34c5..635a2dd0b0 100644 --- a/panda/src/collide/collisionSolid.cxx +++ b/panda/src/collide/collisionSolid.cxx @@ -6,6 +6,9 @@ #include "collisionSolid.h" #include "config_collide.h" #include "collisionEntry.h" +#include "collisionSphere.h" +#include "collisionRay.h" +#include "collisionSegment.h" #include #include @@ -93,30 +96,71 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// // Function: CollisionSolid::test_intersection_from_sphere -// Access: Public, Virtual +// Access: Protected, Virtual // Description: //////////////////////////////////////////////////////////////////// int CollisionSolid:: test_intersection_from_sphere(CollisionHandler *, const CollisionEntry &) const { - collide_cat.warning() - << get_type() << "::test_intersection_from_sphere() called!\n"; + report_undefined_intersection_test(CollisionSphere::get_class_type(), + get_type()); return 0; } //////////////////////////////////////////////////////////////////// // Function: CollisionSolid::test_intersection_from_ray -// Access: Public, Virtual +// Access: Protected, Virtual // Description: //////////////////////////////////////////////////////////////////// int CollisionSolid:: test_intersection_from_ray(CollisionHandler *, const CollisionEntry &) const { - collide_cat.warning() - << get_type() << "::test_intersection_from_ray() called!\n"; + report_undefined_intersection_test(CollisionRay::get_class_type(), + get_type()); return 0; } +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::test_intersection_from_segment +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSolid:: +test_intersection_from_segment(CollisionHandler *, + const CollisionEntry &) const { + report_undefined_intersection_test(CollisionSegment::get_class_type(), + get_type()); + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::report_undefined_intersection_test +// 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_intersection_test(TypeHandle from_type, TypeHandle into_type) { +#ifndef NDEBUG + typedef map Reported; + static Reported reported; + + if (reported.insert(Reported::value_type(from_type, into_type)).second) { + collide_cat.error() + << "Invalid attempt to detect collision from " << from_type << " into " + << into_type << "!\n" + + "This means that a " << from_type << " object attempted to test for a\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"; + } +#endif +} //////////////////////////////////////////////////////////////////// // Function: CollisionSolid::clear_viz_arcs diff --git a/panda/src/collide/collisionSolid.h b/panda/src/collide/collisionSolid.h index 278a21465d..b37c4da7ee 100644 --- a/panda/src/collide/collisionSolid.h +++ b/panda/src/collide/collisionSolid.h @@ -68,6 +68,13 @@ protected: 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); INLINE void mark_viz_stale(); void clear_viz_arcs(); @@ -109,8 +116,9 @@ public: private: static TypeHandle _type_handle; -friend class CollisionSphere; -friend class CollisionRay; + friend class CollisionSphere; + friend class CollisionRay; + friend class CollisionSegment; }; INLINE ostream &operator << (ostream &out, const CollisionSolid &cs) { diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx index f40b38cfef..4191ea5bed 100644 --- a/panda/src/collide/collisionSphere.cxx +++ b/panda/src/collide/collisionSphere.cxx @@ -5,6 +5,7 @@ #include "collisionSphere.h" #include "collisionRay.h" +#include "collisionSegment.h" #include "collisionHandler.h" #include "collisionEntry.h" #include "config_collide.h" @@ -173,6 +174,52 @@ test_intersection_from_ray(CollisionHandler *record, return 1; } +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::test_intersection_from_segment +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSphere:: +test_intersection_from_segment(CollisionHandler *record, + const CollisionEntry &entry) const { + const CollisionSegment *segment; + DCAST_INTO_R(segment, entry.get_from(), 0); + + LPoint3f from_a = segment->get_point_a() * entry.get_wrt_space(); + LVector3f from_b = segment->get_point_b() * entry.get_wrt_space(); + LVector3f from_direction = from_b - from_a; + + double t1, t2; + if (!intersects_line(t1, t2, from_a, from_direction)) { + // No intersection. + return 0; + } + + 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; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LPoint3f into_intersection_point; + if (t1 < 0.0) { + into_intersection_point = from_a + t2 * from_direction; + } else { + into_intersection_point = from_b + t1 * from_direction; + } + new_entry->set_into_intersection_point(into_intersection_point); + + record->add_entry(new_entry); + return 1; +} + //////////////////////////////////////////////////////////////////// // Function: CollisionSphere::recompute_viz // Access: Public, Virtual diff --git a/panda/src/collide/collisionSphere.h b/panda/src/collide/collisionSphere.h index b92156f6f2..1e13007a25 100644 --- a/panda/src/collide/collisionSphere.h +++ b/panda/src/collide/collisionSphere.h @@ -51,6 +51,9 @@ protected: 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 void recompute_viz(Node *parent); diff --git a/panda/src/collide/config_collide.cxx b/panda/src/collide/config_collide.cxx index 9b8e463dc4..6b45630872 100644 --- a/panda/src/collide/config_collide.cxx +++ b/panda/src/collide/config_collide.cxx @@ -4,19 +4,20 @@ //////////////////////////////////////////////////////////////////// #include "config_collide.h" -#include "collisionNode.h" -#include "collisionSolid.h" -#include "collisionSphere.h" -#include "collisionPlane.h" -#include "collisionPolygon.h" -#include "collisionRay.h" #include "collisionEntry.h" #include "collisionHandler.h" #include "collisionHandlerEvent.h" +#include "collisionHandlerFloor.h" #include "collisionHandlerPhysical.h" #include "collisionHandlerPusher.h" -#include "collisionHandlerFloor.h" #include "collisionHandlerQueue.h" +#include "collisionNode.h" +#include "collisionPlane.h" +#include "collisionPolygon.h" +#include "collisionRay.h" +#include "collisionSegment.h" +#include "collisionSolid.h" +#include "collisionSphere.h" #include @@ -24,11 +25,39 @@ Configure(config_collide); NotifyCategoryDef(collide, ""); ConfigureFn(config_collide) { + init_libcollide(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libcollide +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libcollide() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + CollisionEntry::init_type(); + CollisionHandler::init_type(); + CollisionHandlerEvent::init_type(); + CollisionHandlerFloor::init_type(); + CollisionHandlerPhysical::init_type(); + CollisionHandlerPusher::init_type(); + CollisionHandlerQueue::init_type(); CollisionNode::init_type(); - CollisionSolid::init_type(); - CollisionSphere::init_type(); CollisionPlane::init_type(); CollisionPolygon::init_type(); + CollisionRay::init_type(); + CollisionSegment::init_type(); + CollisionSolid::init_type(); + CollisionSphere::init_type(); //Registration of writeable object's creation //functions with BamReader's factory @@ -36,13 +65,4 @@ ConfigureFn(config_collide) { CollisionPlane::register_with_read_factory(); CollisionPolygon::register_with_read_factory(); CollisionSphere::register_with_read_factory(); - CollisionRay::init_type(); - CollisionEntry::init_type(); - CollisionHandler::init_type(); - CollisionHandlerEvent::init_type(); - CollisionHandlerPhysical::init_type(); - CollisionHandlerPusher::init_type(); - CollisionHandlerFloor::init_type(); - CollisionHandlerQueue::init_type(); } - diff --git a/panda/src/collide/config_collide.h b/panda/src/collide/config_collide.h index 41a58c53b7..8399cb48c8 100644 --- a/panda/src/collide/config_collide.h +++ b/panda/src/collide/config_collide.h @@ -11,4 +11,6 @@ NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA); +extern EXPCL_PANDA void init_libcollide(); + #endif