Panda3D
 All Classes Functions Variables Enumerations
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;
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 ////////////////////////////////////////////////////////////////////
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 ////////////////////////////////////////////////////////////////////
129 test_intersection_from_sphere(const CollisionEntry &entry) const {
130  const CollisionSphere *sphere;
131  DCAST_INTO_R(sphere, entry.get_from(), 0);
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 = (
154  has_effective_normal() && sphere->get_respect_effective_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 ////////////////////////////////////////////////////////////////////
170 test_intersection_from_line(const CollisionEntry &entry) const {
171  const CollisionLine *line;
172  DCAST_INTO_R(line, entry.get_from(), 0);
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 =
202  (has_effective_normal() && line->get_respect_effective_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 ////////////////////////////////////////////////////////////////////
217 test_intersection_from_ray(const CollisionEntry &entry) const {
218  const CollisionRay *ray;
219  DCAST_INTO_R(ray, entry.get_from(), 0);
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 =
256  (has_effective_normal() && ray->get_respect_effective_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 ////////////////////////////////////////////////////////////////////
271 test_intersection_from_segment(const CollisionEntry &entry) const {
272  const CollisionSegment *segment;
273  DCAST_INTO_R(segment, entry.get_from(), 0);
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 ////////////////////////////////////////////////////////////////////
332 test_intersection_from_parabola(const CollisionEntry &entry) const {
333  const CollisionParabola *parabola;
334  DCAST_INTO_R(parabola, entry.get_from(), 0);
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::
488 write_datagram(BamWriter *manager, Datagram &me)
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 }
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
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the &quot;origin&quot; of the solid for collision purposes.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
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:438
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.
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
PN_stdfloat get_t2() const
Returns the ending point on the parabola.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
const LParabola & get_parabola() const
Returns the parabola specified by this solid.
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.
virtual void xform(const LMatrix4 &mat)
Transforms the solid by the indicated matrix.
PN_stdfloat get_t1() const
Returns the starting point on the parabola.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:782
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43