Panda3D
collisionPlane.cxx
1 // Filename: collisionPlane.cxx
2 // Created by: drose (25Apr00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 
16 #include "collisionPlane.h"
17 #include "collisionHandler.h"
18 #include "collisionEntry.h"
19 #include "collisionSphere.h"
20 #include "collisionLine.h"
21 #include "collisionRay.h"
22 #include "collisionSegment.h"
23 #include "collisionParabola.h"
24 #include "config_collide.h"
25 #include "pointerToArray.h"
26 #include "geomNode.h"
27 #include "geom.h"
28 #include "datagram.h"
29 #include "datagramIterator.h"
30 #include "bamReader.h"
31 #include "bamWriter.h"
32 #include "boundingPlane.h"
33 #include "geom.h"
34 #include "geomTrifans.h"
35 #include "geomLinestrips.h"
36 #include "geomVertexWriter.h"
37 
38 PStatCollector CollisionPlane::_volume_pcollector("Collision Volumes:CollisionPlane");
39 PStatCollector CollisionPlane::_test_pcollector("Collision Tests:CollisionPlane");
40 TypeHandle CollisionPlane::_type_handle;
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: CollisionPlane::make_copy
44 // Access: Public, Virtual
45 // Description:
46 ////////////////////////////////////////////////////////////////////
47 CollisionSolid *CollisionPlane::
48 make_copy() {
49  return new CollisionPlane(*this);
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: CollisionPlane::xform
54 // Access: Public, Virtual
55 // Description: Transforms the solid by the indicated matrix.
56 ////////////////////////////////////////////////////////////////////
58 xform(const LMatrix4 &mat) {
59  _plane = _plane * mat;
60  CollisionSolid::xform(mat);
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: CollisionPlane::get_collision_origin
65 // Access: Public, Virtual
66 // Description: Returns the point in space deemed to be the "origin"
67 // of the solid for collision purposes. The closest
68 // intersection point to this origin point is considered
69 // to be the most significant.
70 ////////////////////////////////////////////////////////////////////
73  // No real sensible origin exists for a plane. We return 0, 0, 0,
74  // without even bothering to ensure that that point exists on the
75  // plane.
76  return LPoint3::origin();
77 }
78 
79 ////////////////////////////////////////////////////////////////////
80 // Function: CollisionPlane::get_volume_pcollector
81 // Access: Public, Virtual
82 // Description: Returns a PStatCollector that is used to count the
83 // number of bounding volume tests made against a solid
84 // of this type in a given frame.
85 ////////////////////////////////////////////////////////////////////
88  return _volume_pcollector;
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: CollisionPlane::get_test_pcollector
93 // Access: Public, Virtual
94 // Description: Returns a PStatCollector that is used to count the
95 // number of intersection tests made against a solid
96 // of this type in a given frame.
97 ////////////////////////////////////////////////////////////////////
100  return _test_pcollector;
101 }
102 
103 ////////////////////////////////////////////////////////////////////
104 // Function: CollisionPlane::output
105 // Access: Public, Virtual
106 // Description:
107 ////////////////////////////////////////////////////////////////////
108 void CollisionPlane::
109 output(ostream &out) const {
110  out << "cplane, (" << _plane << ")";
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: CollisionPlane::compute_internal_bounds
115 // Access: Protected, Virtual
116 // Description:
117 ////////////////////////////////////////////////////////////////////
118 PT(BoundingVolume) CollisionPlane::
119 compute_internal_bounds() const {
120  return new BoundingPlane(_plane);
121 }
122 
123 ////////////////////////////////////////////////////////////////////
124 // Function: CollisionPlane::test_intersection_from_sphere
125 // Access: Public, Virtual
126 // Description:
127 ////////////////////////////////////////////////////////////////////
128 PT(CollisionEntry) CollisionPlane::
129 test_intersection_from_sphere(const CollisionEntry &entry) const {
130  const CollisionSphere *sphere;
131  DCAST_INTO_R(sphere, entry.get_from(), NULL);
132 
133  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
134 
135  LPoint3 from_center = sphere->get_center() * wrt_mat;
136  LVector3 from_radius_v =
137  LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
138  PN_stdfloat from_radius = length(from_radius_v);
139 
140  PN_stdfloat dist = dist_to_plane(from_center);
141  if (dist > from_radius) {
142  // No intersection.
143  return NULL;
144  }
145 
146  if (collide_cat.is_debug()) {
147  collide_cat.debug()
148  << "intersection detected from " << entry.get_from_node_path()
149  << " into " << entry.get_into_node_path() << "\n";
150  }
151  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
152 
153  LVector3 normal = (
155  ? get_effective_normal() : get_normal();
156 
157  new_entry->set_surface_normal(normal);
158  new_entry->set_surface_point(from_center - get_normal() * dist);
159  new_entry->set_interior_point(from_center - get_normal() * from_radius);
160 
161  return new_entry;
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: CollisionPlane::test_intersection_from_line
166 // Access: Public, Virtual
167 // Description:
168 ////////////////////////////////////////////////////////////////////
169 PT(CollisionEntry) CollisionPlane::
170 test_intersection_from_line(const CollisionEntry &entry) const {
171  const CollisionLine *line;
172  DCAST_INTO_R(line, entry.get_from(), NULL);
173 
174  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
175 
176  LPoint3 from_origin = line->get_origin() * wrt_mat;
177  LVector3 from_direction = line->get_direction() * wrt_mat;
178 
179  PN_stdfloat t;
180  if (!_plane.intersects_line(t, from_origin, from_direction)) {
181  // No intersection. The line is parallel to the plane.
182 
183  if (_plane.dist_to_plane(from_origin) > 0.0f) {
184  // The line is entirely in front of the plane.
185  return NULL;
186  }
187 
188  // The line is entirely behind the plane.
189  t = 0.0f;
190  }
191 
192  if (collide_cat.is_debug()) {
193  collide_cat.debug()
194  << "intersection detected from " << entry.get_from_node_path()
195  << " into " << entry.get_into_node_path() << "\n";
196  }
197  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
198 
199  LPoint3 into_intersection_point = from_origin + t * from_direction;
200 
201  LVector3 normal =
203  ? get_effective_normal() : get_normal();
204 
205  new_entry->set_surface_normal(normal);
206  new_entry->set_surface_point(into_intersection_point);
207 
208  return new_entry;
209 }
210 
211 ////////////////////////////////////////////////////////////////////
212 // Function: CollisionPlane::test_intersection_from_ray
213 // Access: Public, Virtual
214 // Description:
215 ////////////////////////////////////////////////////////////////////
216 PT(CollisionEntry) CollisionPlane::
217 test_intersection_from_ray(const CollisionEntry &entry) const {
218  const CollisionRay *ray;
219  DCAST_INTO_R(ray, entry.get_from(), NULL);
220 
221  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
222 
223  LPoint3 from_origin = ray->get_origin() * wrt_mat;
224  LVector3 from_direction = ray->get_direction() * wrt_mat;
225 
226  PN_stdfloat t;
227 
228  if (_plane.dist_to_plane(from_origin) < 0.0f) {
229  // The origin of the ray is behind the plane, so we don't need to
230  // test further.
231  t = 0.0f;
232 
233  } else {
234  if (!_plane.intersects_line(t, from_origin, from_direction)) {
235  // No intersection. The ray is parallel to the plane.
236  return NULL;
237  }
238 
239  if (t < 0.0f) {
240  // The intersection point is before the start of the ray, and so
241  // the ray is entirely in front of the plane.
242  return NULL;
243  }
244  }
245 
246  if (collide_cat.is_debug()) {
247  collide_cat.debug()
248  << "intersection detected from " << entry.get_from_node_path()
249  << " into " << entry.get_into_node_path() << "\n";
250  }
251  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
252 
253  LPoint3 into_intersection_point = from_origin + t * from_direction;
254 
255  LVector3 normal =
257  ? get_effective_normal() : get_normal();
258 
259  new_entry->set_surface_normal(normal);
260  new_entry->set_surface_point(into_intersection_point);
261 
262  return new_entry;
263 }
264 
265 ////////////////////////////////////////////////////////////////////
266 // Function: CollisionPlane::test_intersection_from_segment
267 // Access: Public, Virtual
268 // Description:
269 ////////////////////////////////////////////////////////////////////
270 PT(CollisionEntry) CollisionPlane::
271 test_intersection_from_segment(const CollisionEntry &entry) const {
272  const CollisionSegment *segment;
273  DCAST_INTO_R(segment, entry.get_from(), NULL);
274 
275  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
276 
277  LPoint3 from_a = segment->get_point_a() * wrt_mat;
278  LPoint3 from_b = segment->get_point_b() * wrt_mat;
279 
280  PN_stdfloat dist_a = _plane.dist_to_plane(from_a);
281  PN_stdfloat dist_b = _plane.dist_to_plane(from_b);
282 
283  if (dist_a >= 0.0f && dist_b >= 0.0f) {
284  // Entirely in front of the plane means no intersection.
285  return NULL;
286  }
287 
288  if (collide_cat.is_debug()) {
289  collide_cat.debug()
290  << "intersection detected from " << entry.get_from_node_path()
291  << " into " << entry.get_into_node_path() << "\n";
292  }
293  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
294 
295  LVector3 normal = (has_effective_normal() && segment->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
296  new_entry->set_surface_normal(normal);
297 
298  PN_stdfloat t;
299  LVector3 from_direction = from_b - from_a;
300  if (_plane.intersects_line(t, from_a, from_direction)) {
301  // It intersects the plane.
302  if (t >= 0.0f && t <= 1.0f) {
303  // Within the segment! Yay, that means we have a surface point.
304  new_entry->set_surface_point(from_a + t * from_direction);
305  }
306  }
307 
308  if (dist_a < dist_b) {
309  // Point A penetrates deeper.
310  new_entry->set_interior_point(from_a);
311 
312  } else if (dist_b < dist_a) {
313  // No, point B does.
314  new_entry->set_interior_point(from_b);
315 
316  } else {
317  // Let's be fair and choose the center of the segment.
318  new_entry->set_interior_point((from_a + from_b) * 0.5);
319  }
320 
321  return new_entry;
322 }
323 
324 ////////////////////////////////////////////////////////////////////
325 // Function: CollisionPlane::test_intersection_from_parabola
326 // Access: Public, Virtual
327 // Description: This is part of the double-dispatch implementation of
328 // test_intersection(). It is called when the "from"
329 // object is a parabola.
330 ////////////////////////////////////////////////////////////////////
331 PT(CollisionEntry) CollisionPlane::
332 test_intersection_from_parabola(const CollisionEntry &entry) const {
333  const CollisionParabola *parabola;
334  DCAST_INTO_R(parabola, entry.get_from(), NULL);
335 
336  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
337 
338  // Convert the parabola into local coordinate space.
339  LParabola local_p(parabola->get_parabola());
340  local_p.xform(wrt_mat);
341 
342  PN_stdfloat t;
343  if (_plane.dist_to_plane(local_p.calc_point(parabola->get_t1())) < 0.0f) {
344  // The first point in the parabola is behind the plane, so we
345  // don't need to test further.
346  t = parabola->get_t1();
347 
348  } else {
349  PN_stdfloat t1, t2;
350  if (!get_plane().intersects_parabola(t1, t2, local_p)) {
351  // No intersection. The infinite parabola is entirely in front
352  // of the plane.
353  return NULL;
354  }
355 
356  if (t1 >= parabola->get_t1() && t1 <= parabola->get_t2()) {
357  if (t2 >= parabola->get_t1() && t2 <= parabola->get_t2()) {
358  // Both intersection points are within our segment of the
359  // parabola. Choose the first of the two.
360  t = min(t1, t2);
361  } else {
362  // Only t1 is within our segment.
363  t = t1;
364  }
365 
366  } else if (t2 >= parabola->get_t1() && t2 <= parabola->get_t2()) {
367  // Only t2 is within our segment.
368  t = t2;
369 
370  } else {
371  // Neither intersection point is within our segment.
372  return NULL;
373  }
374  }
375 
376  if (collide_cat.is_debug()) {
377  collide_cat.debug()
378  << "intersection detected from " << entry.get_from_node_path()
379  << " into " << entry.get_into_node_path() << "\n";
380  }
381  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
382 
383  LPoint3 into_intersection_point = local_p.calc_point(t);
384 
385  LVector3 normal = (has_effective_normal() && parabola->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
386 
387  new_entry->set_surface_normal(normal);
388  new_entry->set_surface_point(into_intersection_point);
389 
390  return new_entry;
391 }
392 
393 ////////////////////////////////////////////////////////////////////
394 // Function: CollisionPlane::fill_viz_geom
395 // Access: Protected, Virtual
396 // Description: Fills the _viz_geom GeomNode up with Geoms suitable
397 // for rendering this solid.
398 ////////////////////////////////////////////////////////////////////
399 void CollisionPlane::
400 fill_viz_geom() {
401  if (collide_cat.is_debug()) {
402  collide_cat.debug()
403  << "Recomputing viz for " << *this << "\n";
404  }
405 
406  // Since we can't represent an infinite plane, we'll have to be
407  // satisfied with drawing a big polygon. Choose four points on the
408  // plane to be the corners of the polygon.
409 
410  // We must choose four points fairly reasonably spread apart on
411  // the plane. We'll start with a center point and one corner
412  // point, and then use cross products to find the remaining three
413  // corners of a square.
414 
415  // The center point will be on the axis with the largest
416  // coefficent. The first corner will be diagonal in the other two
417  // dimensions.
418 
419  LPoint3 cp;
420  LVector3 p1, p2, p3, p4;
421 
422  LVector3 normal = get_normal();
423  PN_stdfloat D = _plane[3];
424 
425  if (fabs(normal[0]) > fabs(normal[1]) &&
426  fabs(normal[0]) > fabs(normal[2])) {
427  // X has the largest coefficient.
428  cp.set(-D / normal[0], 0.0f, 0.0f);
429  p1 = LPoint3(-(normal[1] + normal[2] + D)/normal[0], 1.0f, 1.0f) - cp;
430 
431  } else if (fabs(normal[1]) > fabs(normal[2])) {
432  // Y has the largest coefficient.
433  cp.set(0.0f, -D / normal[1], 0.0f);
434  p1 = LPoint3(1.0f, -(normal[0] + normal[2] + D)/normal[1], 1.0f) - cp;
435 
436  } else {
437  // Z has the largest coefficient.
438  cp.set(0.0f, 0.0f, -D / normal[2]);
439  p1 = LPoint3(1.0f, 1.0f, -(normal[0] + normal[1] + D)/normal[2]) - cp;
440  }
441 
442  p1.normalize();
443  p2 = cross(normal, p1);
444  p3 = cross(normal, p2);
445  p4 = cross(normal, p3);
446 
447  static const double plane_scale = 10.0;
448 
449  PT(GeomVertexData) vdata = new GeomVertexData
450  ("collision", GeomVertexFormat::get_v3(),
451  Geom::UH_static);
452  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
453 
454  vertex.add_data3(cp + p1 * plane_scale);
455  vertex.add_data3(cp + p2 * plane_scale);
456  vertex.add_data3(cp + p3 * plane_scale);
457  vertex.add_data3(cp + p4 * plane_scale);
458 
459  PT(GeomTrifans) body = new GeomTrifans(Geom::UH_static);
460  body->add_consecutive_vertices(0, 4);
461  body->close_primitive();
462 
463  PT(GeomLinestrips) border = new GeomLinestrips(Geom::UH_static);
464  border->add_consecutive_vertices(0, 4);
465  border->add_vertex(0);
466  border->close_primitive();
467 
468  PT(Geom) geom1 = new Geom(vdata);
469  geom1->add_primitive(body);
470 
471  PT(Geom) geom2 = new Geom(vdata);
472  geom2->add_primitive(border);
473 
474  _viz_geom->add_geom(geom1, get_solid_viz_state());
475  _viz_geom->add_geom(geom2, get_wireframe_viz_state());
476 
477  _bounds_viz_geom->add_geom(geom1, get_solid_bounds_viz_state());
478  _bounds_viz_geom->add_geom(geom2, get_wireframe_bounds_viz_state());
479 }
480 
481 ////////////////////////////////////////////////////////////////////
482 // Function: CollisionPlane::write_datagram
483 // Access: Public
484 // Description: Function to write the important information in
485 // the particular object to a Datagram
486 ////////////////////////////////////////////////////////////////////
487 void CollisionPlane::
489 {
490  CollisionSolid::write_datagram(manager, me);
491  _plane.write_datagram(me);
492 }
493 
494 ////////////////////////////////////////////////////////////////////
495 // Function: CollisionPlane::fillin
496 // Access: Protected
497 // Description: Function that reads out of the datagram (or asks
498 // manager to read) all of the data that is needed to
499 // re-create this object and stores it in the appropiate
500 // place
501 ////////////////////////////////////////////////////////////////////
502 void CollisionPlane::
503 fillin(DatagramIterator& scan, BamReader* manager)
504 {
505  CollisionSolid::fillin(scan, manager);
506  _plane.read_datagram(scan);
507 }
508 
509 ////////////////////////////////////////////////////////////////////
510 // Function: CollisionPlane::make_CollisionPlane
511 // Access: Protected
512 // Description: Factory method to generate a CollisionPlane object
513 ////////////////////////////////////////////////////////////////////
516 {
517  CollisionPlane *me = new CollisionPlane;
518  DatagramIterator scan;
519  BamReader *manager;
520 
521  parse_params(params, scan, manager);
522  me->fillin(scan, manager);
523  return me;
524 }
525 
526 ////////////////////////////////////////////////////////////////////
527 // Function: CollisionPlane::register_with_factory
528 // Access: Public, Static
529 // Description: Factory method to generate a CollisionPlane object
530 ////////////////////////////////////////////////////////////////////
531 void CollisionPlane::
533 {
535 }
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
const LParabola & get_parabola() const
Returns the parabola specified by this solid.
PN_stdfloat get_t1() const
Returns the starting point on the parabola.
const LVector3 & get_effective_normal() const
Returns the normal that was set by set_effective_normal().
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
An infinite ray, with a specific origin and direction.
Definition: collisionRay.h:31
NodePath get_into_node_path() const
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
PN_stdfloat get_t2() const
Returns the ending point on the parabola.
The abstract base class for all things that can collide with other things in the world, and all the things they can collide with (except geometry).
Defines a series of triangle fans.
Definition: geomTrifans.h:25
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
A spherical collision volume or object.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
static const LPoint3f & origin(CoordinateSystem cs=CS_default)
Returns the origin of the indicated coordinate system.
Definition: lpoint3.h:451
static TypedWritable * make_CollisionPlane(const FactoryParams &params)
Factory method to generate a CollisionPlane object.
This funny bounding volume is an infinite plane that divides space into two regions: the part behind ...
Definition: boundingPlane.h:31
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats...
virtual void xform(const LMatrix4 &mat)
Transforms the solid by the indicated matrix.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
A finite line segment, with two specific endpoints but no thickness.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
Defines a single collision event.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
LVecBase4f xform(const LVecBase4f &v) const
4-component vector or point times matrix.
Definition: lmatrix.h:1647
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
An infinite line, similar to a CollisionRay, except that it extends in both directions.
Definition: collisionLine.h:28
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
static void register_with_read_factory()
Factory method to generate a CollisionPlane object.
This defines a parabolic arc, or subset of an arc, similar to the path of a projectile or falling obj...
Defines a series of line strips.
bool has_effective_normal() const
Returns true if a special normal was set by set_effective_normal(), false otherwise.
A class to retrieve the individual data elements previously stored in a Datagram. ...
NodePath get_from_node_path() const
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
bool get_respect_effective_normal() const
See set_respect_effective_normal().
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
const CollisionSolid * get_from() const
Returns the CollisionSolid pointer for the particular solid that triggered this collision.