Panda3D
collisionSphere.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file collisionSphere.cxx
10  * @author drose
11  * @date 2000-04-24
12  */
13 
14 #include "collisionSphere.h"
15 #include "collisionLine.h"
16 #include "collisionRay.h"
17 #include "collisionHandler.h"
18 #include "collisionEntry.h"
19 #include "collisionSegment.h"
20 #include "collisionCapsule.h"
21 #include "collisionParabola.h"
22 #include "collisionBox.h"
23 #include "config_collide.h"
24 #include "boundingSphere.h"
25 #include "datagram.h"
26 #include "datagramIterator.h"
27 #include "bamReader.h"
28 #include "bamWriter.h"
29 #include "nearly_zero.h"
30 #include "cmath.h"
31 #include "mathNumbers.h"
32 #include "geom.h"
33 #include "geomTristrips.h"
34 #include "geomVertexWriter.h"
35 
36 using std::max;
37 using std::min;
38 
39 PStatCollector CollisionSphere::_volume_pcollector(
40  "Collision Volumes:CollisionSphere");
41 PStatCollector CollisionSphere::_test_pcollector(
42  "Collision Tests:CollisionSphere");
43 TypeHandle CollisionSphere::_type_handle;
44 
45 /**
46  *
47  */
48 CollisionSolid *CollisionSphere::
49 make_copy() {
50  return new CollisionSphere(*this);
51 }
52 
53 /**
54  *
55  */
56 PT(CollisionEntry) CollisionSphere::
57 test_intersection(const CollisionEntry &entry) const {
58  return entry.get_into()->test_intersection_from_sphere(entry);
59 }
60 
61 /**
62  * Transforms the solid by the indicated matrix.
63  */
64 void CollisionSphere::
65 xform(const LMatrix4 &mat) {
66  _center = _center * mat;
67 
68  // This is a little cheesy and fails miserably in the presence of a non-
69  // uniform scale.
70  LVector3 radius_v = LVector3(_radius, 0.0f, 0.0f) * mat;
71  _radius = length(radius_v);
72  mark_viz_stale();
73  mark_internal_bounds_stale();
74 }
75 
76 /**
77  * Returns the point in space deemed to be the "origin" of the solid for
78  * collision purposes. The closest intersection point to this origin point is
79  * considered to be the most significant.
80  */
82 get_collision_origin() const {
83  return get_center();
84 }
85 
86 /**
87  * Returns a PStatCollector that is used to count the number of bounding
88  * volume tests made against a solid of this type in a given frame.
89  */
92  return _volume_pcollector;
93 }
94 
95 /**
96  * Returns a PStatCollector that is used to count the number of intersection
97  * tests made against a solid of this type in a given frame.
98  */
101  return _test_pcollector;
102 }
103 
104 /**
105  *
106  */
107 void CollisionSphere::
108 output(std::ostream &out) const {
109  out << "sphere, c (" << get_center() << "), r " << get_radius();
110 }
111 
112 /**
113  *
114  */
115 PT(BoundingVolume) CollisionSphere::
116 compute_internal_bounds() const {
117  return new BoundingSphere(_center, _radius);
118 }
119 
120 /**
121  *
122  */
123 PT(CollisionEntry) CollisionSphere::
124 test_intersection_from_sphere(const CollisionEntry &entry) const {
125  const CollisionSphere *sphere;
126  DCAST_INTO_R(sphere, entry.get_from(), nullptr);
127 
128  CPT(TransformState) wrt_space = entry.get_wrt_space();
129 
130  const LMatrix4 &wrt_mat = wrt_space->get_mat();
131 
132  LPoint3 from_b = sphere->get_center() * wrt_mat;
133 
134  LPoint3 into_center(get_center());
135  PN_stdfloat into_radius(get_radius());
136 
137  LVector3 from_radius_v =
138  LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
139  PN_stdfloat from_radius = length(from_radius_v);
140 
141  LPoint3 into_intersection_point(from_b);
142  double t1, t2;
143  LPoint3 contact_point(into_intersection_point);
144  PN_stdfloat actual_t = 0.0f;
145 
146  LVector3 vec = from_b - into_center;
147  PN_stdfloat dist2 = dot(vec, vec);
148  if (dist2 > (into_radius + from_radius) * (into_radius + from_radius)) {
149  // No intersection with the current position. Check the delta from the
150  // previous frame.
151  CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
152  LPoint3 from_a = sphere->get_center() * wrt_prev_space->get_mat();
153 
154  if (!from_a.almost_equal(from_b)) {
155  LVector3 from_direction = from_b - from_a;
156  if (!intersects_line(t1, t2, from_a, from_direction, from_radius)) {
157  // No intersection.
158  return nullptr;
159  }
160 
161  if (t2 < 0.0 || t1 > 1.0) {
162  // Both intersection points are before the start of the segment or
163  // after the end of the segment.
164  return nullptr;
165  }
166 
167  // doubles, not floats, to satisfy min and max templates.
168  actual_t = min(1.0, max(0.0, t1));
169  contact_point = from_a + actual_t * (from_b - from_a);
170 
171  if (t1 < 0.0) {
172  // Point a is within the sphere. The first intersection point is
173  // point a itself.
174  into_intersection_point = from_a;
175  } else {
176  // Point a is outside the sphere, and point b is either inside the
177  // sphere or beyond it. The first intersection point is at t1.
178  into_intersection_point = from_a + t1 * from_direction;
179  }
180  } else {
181  // No delta, therefore no intersection.
182  return nullptr;
183  }
184  }
185 
186  if (collide_cat.is_debug()) {
187  collide_cat.debug()
188  << "intersection detected from " << entry.get_from_node_path()
189  << " into " << entry.get_into_node_path() << "\n";
190  }
191  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
192 
193  LPoint3 from_center = sphere->get_center() * wrt_mat;
194 
195  LVector3 surface_normal;
196  LVector3 v(into_intersection_point - into_center);
197  PN_stdfloat vec_length = v.length();
198  if (IS_NEARLY_ZERO(vec_length)) {
199  // If we don't have a collision normal (e.g. the centers are exactly
200  // coincident), then make up an arbitrary normal--any one is as good as
201  // any other.
202  surface_normal.set(1.0, 0.0, 0.0);
203  } else {
204  surface_normal = v / vec_length;
205  }
206 
207  LVector3 eff_normal = (has_effective_normal() && sphere->get_respect_effective_normal()) ? get_effective_normal() : surface_normal;
208 
209  LVector3 contact_normal;
210  LVector3 v2 = contact_point - into_center;
211  PN_stdfloat v2_len = v2.length();
212  if (IS_NEARLY_ZERO(v2_len)) {
213  // If we don't have a collision normal (e.g. the centers are exactly
214  // coincident), then make up an arbitrary normal--any one is as good as
215  // any other.
216  contact_normal.set(1.0, 0.0, 0.0);
217  } else {
218  contact_normal = v2 / v2_len;
219  }
220 
221  new_entry->set_surface_normal(eff_normal);
222  new_entry->set_surface_point(into_center + surface_normal * into_radius);
223  new_entry->set_interior_point(from_center - surface_normal * from_radius);
224  new_entry->set_contact_pos(contact_point);
225  new_entry->set_contact_normal(contact_normal);
226  new_entry->set_t(actual_t);
227 
228  return new_entry;
229 }
230 
231 /**
232  *
233  */
234 PT(CollisionEntry) CollisionSphere::
235 test_intersection_from_line(const CollisionEntry &entry) const {
236  const CollisionLine *line;
237  DCAST_INTO_R(line, entry.get_from(), nullptr);
238 
239  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
240 
241  LPoint3 from_origin = line->get_origin() * wrt_mat;
242  LVector3 from_direction = line->get_direction() * wrt_mat;
243 
244  double t1, t2;
245  if (!intersects_line(t1, t2, from_origin, from_direction, 0.0f)) {
246  // No intersection.
247  return nullptr;
248  }
249 
250  if (collide_cat.is_debug()) {
251  collide_cat.debug()
252  << "intersection detected from " << entry.get_from_node_path()
253  << " into " << entry.get_into_node_path() << "\n";
254  }
255  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
256 
257  LPoint3 into_intersection_point = from_origin + t1 * from_direction;
258  new_entry->set_surface_point(into_intersection_point);
259 
261  new_entry->set_surface_normal(get_effective_normal());
262  } else {
263  LVector3 normal = into_intersection_point - get_center();
264  normal.normalize();
265  new_entry->set_surface_normal(normal);
266  }
267 
268  return new_entry;
269 }
270 
271 /**
272  * Double dispatch point for box as a FROM object
273  */
274 PT(CollisionEntry) CollisionSphere::
275 test_intersection_from_box(const CollisionEntry &entry) const {
276  const CollisionBox *box;
277  DCAST_INTO_R(box, entry.get_from(), nullptr);
278 
279  // Instead of transforming the box into the sphere's coordinate space, we do
280  // it the other way around. It's easier that way.
281  const LMatrix4 &wrt_mat = entry.get_inv_wrt_mat();
282 
283  LPoint3 center = wrt_mat.xform_point(_center);
284  PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, _radius)).length_squared();
285 
286  LPoint3 box_min = box->get_min();
287  LPoint3 box_max = box->get_max();
288 
289  // Arvo's algorithm.
290  PN_stdfloat d = 0;
291  PN_stdfloat s;
292 
293  if (center[0] < box_min[0]) {
294  s = center[0] - box_min[0];
295  d += s * s;
296 
297  } else if (center[0] > box_max[0]) {
298  s = center[0] - box_max[0];
299  d += s * s;
300  }
301 
302  if (center[1] < box_min[1]) {
303  s = center[1] - box_min[1];
304  d += s * s;
305 
306  } else if (center[1] > box_max[1]) {
307  s = center[1] - box_max[1];
308  d += s * s;
309  }
310 
311  if (center[2] < box_min[2]) {
312  s = center[2] - box_min[2];
313  d += s * s;
314 
315  } else if (center[2] > box_max[2]) {
316  s = center[2] - box_max[2];
317  d += s * s;
318  }
319 
320  if (d > radius_sq) {
321  return nullptr;
322  }
323 
324  if (collide_cat.is_debug()) {
325  collide_cat.debug()
326  << "intersection detected from " << entry.get_from_node_path()
327  << " into " << entry.get_into_node_path() << "\n";
328  }
329 
330  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
331 
332  // To get the interior point, clamp the sphere center to the AABB.
333  LPoint3 interior = entry.get_wrt_mat().xform_point(center.fmax(box_min).fmin(box_max));
334  new_entry->set_interior_point(interior);
335 
336  // Now extrapolate the surface point and normal from that.
337  LVector3 normal = interior - _center;
338  normal.normalize();
339  new_entry->set_surface_point(_center + normal * _radius);
340  new_entry->set_surface_normal(
342  ? get_effective_normal() : normal);
343 
344  return new_entry;
345 }
346 
347 /**
348  *
349  */
350 PT(CollisionEntry) CollisionSphere::
351 test_intersection_from_ray(const CollisionEntry &entry) const {
352  const CollisionRay *ray;
353  DCAST_INTO_R(ray, entry.get_from(), nullptr);
354 
355  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
356 
357  LPoint3 from_origin = ray->get_origin() * wrt_mat;
358  LVector3 from_direction = ray->get_direction() * wrt_mat;
359 
360  double t1, t2;
361  if (!intersects_line(t1, t2, from_origin, from_direction, 0.0f)) {
362  // No intersection.
363  return nullptr;
364  }
365 
366  if (t2 < 0.0) {
367  // Both intersection points are before the start of the ray.
368  return nullptr;
369  }
370 
371  t1 = max(t1, 0.0);
372 
373  if (collide_cat.is_debug()) {
374  collide_cat.debug()
375  << "intersection detected from " << entry.get_from_node_path()
376  << " into " << entry.get_into_node_path() << "\n";
377  }
378  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
379 
380  LPoint3 into_intersection_point = from_origin + t1 * from_direction;
381  new_entry->set_surface_point(into_intersection_point);
382 
384  new_entry->set_surface_normal(get_effective_normal());
385  } else {
386  LVector3 normal = into_intersection_point - get_center();
387  normal.normalize();
388  new_entry->set_surface_normal(normal);
389  }
390 
391  return new_entry;
392 }
393 
394 /**
395  *
396  */
397 PT(CollisionEntry) CollisionSphere::
398 test_intersection_from_segment(const CollisionEntry &entry) const {
399  const CollisionSegment *segment;
400  DCAST_INTO_R(segment, entry.get_from(), nullptr);
401 
402  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
403 
404  LPoint3 from_a = segment->get_point_a() * wrt_mat;
405  LPoint3 from_b = segment->get_point_b() * wrt_mat;
406  LVector3 from_direction = from_b - from_a;
407 
408  double t1, t2;
409  if (!intersects_line(t1, t2, from_a, from_direction, 0.0f)) {
410  // No intersection.
411  return nullptr;
412  }
413 
414  if (t2 < 0.0 || t1 > 1.0) {
415  // Both intersection points are before the start of the segment or after
416  // the end of the segment.
417  return nullptr;
418  }
419 
420  t1 = max(t1, 0.0);
421 
422  if (collide_cat.is_debug()) {
423  collide_cat.debug()
424  << "intersection detected from " << entry.get_from_node_path()
425  << " into " << entry.get_into_node_path() << "\n";
426  }
427  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
428 
429  LPoint3 into_intersection_point = from_a + t1 * from_direction;
430  new_entry->set_surface_point(into_intersection_point);
431 
433  new_entry->set_surface_normal(get_effective_normal());
434  } else {
435  LVector3 normal = into_intersection_point - get_center();
436  normal.normalize();
437  new_entry->set_surface_normal(normal);
438  }
439 
440  return new_entry;
441 }
442 
443 /**
444  *
445  */
446 PT(CollisionEntry) CollisionSphere::
447 test_intersection_from_capsule(const CollisionEntry &entry) const {
448  const CollisionCapsule *capsule;
449  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
450 
451  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
452 
453  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
454  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
455  LVector3 from_direction = from_b - from_a;
456 
457  LVector3 from_radius_v =
458  LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
459  PN_stdfloat from_radius = length(from_radius_v);
460 
461  double t1, t2;
462  if (!intersects_line(t1, t2, from_a, from_direction, from_radius)) {
463  // No intersection.
464  return nullptr;
465  }
466 
467  if (t2 < 0.0 || t1 > 1.0) {
468  // Both intersection points are before the start of the capsule or after
469  // the end of the capsule.
470  return nullptr;
471  }
472 
473  PN_stdfloat t = (t1 + t2) * (PN_stdfloat)0.5;
474  t = max(t, (PN_stdfloat)0.0);
475  t = min(t, (PN_stdfloat)1.0);
476  LPoint3 inner_point = from_a + t * from_direction;
477 
478  if (collide_cat.is_debug()) {
479  collide_cat.debug()
480  << "intersection detected from " << entry.get_from_node_path()
481  << " into " << entry.get_into_node_path() << "\n";
482  }
483  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
484 
485  LVector3 normal = inner_point - get_center();
486  normal.normalize();
487  new_entry->set_surface_point(get_center() + normal * get_radius());
488  new_entry->set_interior_point(inner_point - normal * from_radius);
489 
491  new_entry->set_surface_normal(get_effective_normal());
492  } else {
493  new_entry->set_surface_normal(normal);
494  }
495 
496  return new_entry;
497 }
498 
499 /**
500  *
501  */
502 PT(CollisionEntry) CollisionSphere::
503 test_intersection_from_parabola(const CollisionEntry &entry) const {
504  const CollisionParabola *parabola;
505  DCAST_INTO_R(parabola, entry.get_from(), nullptr);
506 
507  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
508 
509  // Convert the parabola into local coordinate space.
510  LParabola local_p(parabola->get_parabola());
511  local_p.xform(wrt_mat);
512 
513  double t;
514  if (!intersects_parabola(t, local_p, parabola->get_t1(), parabola->get_t2(),
515  local_p.calc_point(parabola->get_t1()),
516  local_p.calc_point(parabola->get_t2()))) {
517  // No intersection.
518  return nullptr;
519  }
520 
521  if (collide_cat.is_debug()) {
522  collide_cat.debug()
523  << "intersection detected from " << entry.get_from_node_path()
524  << " into " << entry.get_into_node_path() << "\n";
525  }
526  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
527 
528  LPoint3 into_intersection_point = local_p.calc_point(t);
529  new_entry->set_surface_point(into_intersection_point);
530 
531  if (has_effective_normal() && parabola->get_respect_effective_normal()) {
532  new_entry->set_surface_normal(get_effective_normal());
533  } else {
534  LVector3 normal = into_intersection_point - get_center();
535  normal.normalize();
536  new_entry->set_surface_normal(normal);
537  }
538 
539  return new_entry;
540 }
541 
542 /**
543  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
544  * solid.
545  */
546 void CollisionSphere::
547 fill_viz_geom() {
548  if (collide_cat.is_debug()) {
549  collide_cat.debug()
550  << "Recomputing viz for " << *this << "\n";
551  }
552 
553  static const int num_slices = 16;
554  static const int num_stacks = 8;
555 
556  PT(GeomVertexData) vdata = new GeomVertexData
557  ("collision", GeomVertexFormat::get_v3(),
558  Geom::UH_static);
559  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
560 
561  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
562  for (int sl = 0; sl < num_slices; ++sl) {
563  PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices;
564  PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices;
565  vertex.add_data3(compute_point(0.0, longitude0));
566  for (int st = 1; st < num_stacks; ++st) {
567  PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks;
568  vertex.add_data3(compute_point(latitude, longitude0));
569  vertex.add_data3(compute_point(latitude, longitude1));
570  }
571  vertex.add_data3(compute_point(1.0, longitude0));
572 
573  strip->add_next_vertices(num_stacks * 2);
574  strip->close_primitive();
575  }
576 
577  PT(Geom) geom = new Geom(vdata);
578  geom->add_primitive(strip);
579 
580  _viz_geom->add_geom(geom, get_solid_viz_state());
581  _bounds_viz_geom->add_geom(geom, get_solid_bounds_viz_state());
582 }
583 
584 /**
585  * Determine the point(s) of intersection of a parametric line with the
586  * sphere. The line is infinite in both directions, and passes through "from"
587  * and from+delta. If the line does not intersect the sphere, the function
588  * returns false, and t1 and t2 are undefined. If it does intersect the
589  * sphere, it returns true, and t1 and t2 are set to the points along the
590  * equation from+t*delta that correspond to the two points of intersection.
591  */
592 bool CollisionSphere::
593 intersects_line(double &t1, double &t2,
594  const LPoint3 &from, const LVector3 &delta,
595  PN_stdfloat inflate_radius) const {
596  // Solve the equation for the intersection of a line with a sphere using the
597  // quadratic equation.
598 
599  // A line segment from f to f+d is defined as all P such that P = f + td for
600  // 0 <= t <= 1.
601 
602  // A sphere with radius r about point c is defined as all P such that r^2 =
603  // (P - c)^2.
604 
605  // Substituting P in the above we have:
606 
607  // r^2 = (f + td - c)^2 = (f^2 + ftd - fc + ftd + t^2d^2 - tdc - fc - tdc +
608  // c^2) = t^2(d^2) + t(fd + fd - dc - dc) + (f^2 - fc - fc + c^2) = t^2(d^2)
609  // + t(2d(f - c)) + (f - c)^2
610 
611  // Thus, the equation is quadratic in t, and we have at^2 + bt + c = 0
612 
613  // Where a = d^2 b = 2d(f - c) c = (f - c)^2 - r^2
614 
615  // Solving for t using the quadratic equation gives us the point of
616  // intersection along the line segment. Actually, there are two solutions
617  // (since it is quadratic): one for the front of the sphere, and one for the
618  // back. In the case where the line is tangent to the sphere, there is only
619  // one solution (and the radical is zero).
620 
621  double A = dot(delta, delta);
622 
623  LVector3 fc = from - get_center();
624  double fc_d2 = dot(fc, fc);
625  double radius = get_radius() + inflate_radius;
626  double C = fc_d2 - radius * radius;
627 
628  if (A == 0.0) {
629  // Degenerate case where delta is zero. This is effectively a test
630  // against a point (or sphere, for nonzero inflate_radius).
631  t1 = 0.0;
632  t2 = 0.0;
633  return C < 0.0;
634  }
635 
636  double B = 2.0f * dot(delta, fc);
637  double radical = B*B - 4.0*A*C;
638 
639  if (IS_NEARLY_ZERO(radical)) {
640  // Tangent.
641  t1 = t2 = -B /(2.0*A);
642  return true;
643 
644  } else if (radical < 0.0) {
645  // No real roots: no intersection with the line.
646  return false;
647  }
648 
649  double reciprocal_2A = 1.0/(2.0*A);
650  double sqrt_radical = sqrtf(radical);
651  t1 = ( -B - sqrt_radical ) * reciprocal_2A;
652  t2 = ( -B + sqrt_radical ) * reciprocal_2A;
653 
654  return true;
655 }
656 
657 /**
658  * Determine a point of intersection of a parametric parabola with the sphere.
659  *
660  * We only consider the segment of the parabola between t1 and t2, which has
661  * already been computed as corresponding to points p1 and p2. If there is an
662  * intersection, t is set to the parametric point of intersection, and true is
663  * returned; otherwise, false is returned.
664  */
665 bool CollisionSphere::
666 intersects_parabola(double &t, const LParabola &parabola,
667  double t1, double t2,
668  const LPoint3 &p1, const LPoint3 &p2) const {
669  if (t1 == t2) {
670  // Special case: a single point.
671  if ((p1 - _center).length_squared() > _radius * _radius) {
672  // No intersection.
673  return false;
674  }
675  t = t1;
676  return true;
677  }
678 
679  // To directly test for intersection between a parabola (quadratic) and a
680  // sphere (also quadratic) requires solving a quartic equation. Doable, but
681  // hard, and I'm a programmer, not a mathematician. So I'll solve it the
682  // programmer's way instead, by approximating the parabola with a series of
683  // line segments. Hence, this function works by recursively subdividing the
684  // parabola as necessary.
685 
686  // First, see if the line segment (p1 - p2) comes sufficiently close to the
687  // parabola. Do this by computing the parametric intervening point and
688  // comparing its distance from the linear intervening point.
689  double tmid = (t1 + t2) * 0.5;
690  if (tmid != t1 && tmid != t2) {
691  LPoint3 pmid = parabola.calc_point(tmid);
692  LPoint3 pmid2 = (p1 + p2) * 0.5f;
693 
694  if ((pmid - pmid2).length_squared() > 0.001f) {
695  // Subdivide.
696  if (intersects_parabola(t, parabola, t1, tmid, p1, pmid)) {
697  return true;
698  }
699  return intersects_parabola(t, parabola, tmid, t2, pmid, p2);
700  }
701  }
702 
703  // The line segment is sufficiently close; compare the segment itself.
704  double t1a, t2a;
705  if (!intersects_line(t1a, t2a, p1, p2 - p1, 0.0f)) {
706  return false;
707  }
708 
709  if (t2a < 0.0 || t1a > 1.0) {
710  return false;
711  }
712 
713  t = max(t1a, 0.0);
714  return true;
715 }
716 
717 /**
718  * Returns a point on the surface of the sphere. latitude and longitude range
719  * from 0.0 to 1.0. This is used by fill_viz_geom() to create a visible
720  * representation of the sphere.
721  */
722 LVertex CollisionSphere::
723 compute_point(PN_stdfloat latitude, PN_stdfloat longitude) const {
724  PN_stdfloat s1, c1;
725  csincos(latitude * MathNumbers::pi, &s1, &c1);
726 
727  PN_stdfloat s2, c2;
728  csincos(longitude * 2.0f * MathNumbers::pi, &s2, &c2);
729 
730  LVertex p(s1 * c2, s1 * s2, c1);
731  return p * get_radius() + get_center();
732 }
733 
734 /**
735  * Factory method to generate a CollisionSphere object
736  */
739  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionSphere);
740 }
741 
742 /**
743  * Function to write the important information in the particular object to a
744  * Datagram
745  */
747 write_datagram(BamWriter *manager, Datagram &me) {
748  CollisionSolid::write_datagram(manager, me);
749  _center.write_datagram(me);
750  me.add_stdfloat(_radius);
751 }
752 
753 /**
754  * Factory method to generate a CollisionSphere object
755  */
756 TypedWritable *CollisionSphere::
757 make_CollisionSphere(const FactoryParams &params) {
759  DatagramIterator scan;
760  BamReader *manager;
761 
762  parse_params(params, scan, manager);
763  me->fillin(scan, manager);
764  return me;
765 }
766 
767 /**
768  * Function that reads out of the datagram (or asks manager to read) all of
769  * the data that is needed to re-create this object and stores it in the
770  * appropiate place
771  */
772 void CollisionSphere::
773 fillin(DatagramIterator& scan, BamReader* manager) {
774  CollisionSolid::fillin(scan, manager);
775  _center.read_datagram(scan);
776  _radius = scan.get_stdfloat();
777 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A cuboid collision volume or object.
Definition: collisionBox.h:27
This implements a solid consisting of a cylinder with hemispherical endcaps, also known as a capsule ...
Defines a single collision event.
get_into
Returns the CollisionSolid pointer for the particular solid was collided into.
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
get_from
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
An infinite line, similar to a CollisionRay, except that it extends in both directions.
Definition: collisionLine.h:25
This defines a parabolic arc, or subset of an arc, similar to the path of a projectile or falling obj...
get_t1
Returns the starting point on the parabola.
get_t2
Returns the ending point on the parabola.
get_parabola
Returns the parabola specified by this solid.
An infinite ray, with a specific origin and direction.
Definition: collisionRay.h:27
A finite line segment, with two specific endpoints but no thickness.
The abstract base class for all things that can collide with other things in the world,...
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
bool has_effective_normal() const
Returns true if a special normal was set by set_effective_normal(), false otherwise.
const LVector3 & get_effective_normal() const
Returns the normal that was set by set_effective_normal().
get_respect_effective_normal
See set_respect_effective_normal().
A spherical collision volume or object.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
static void register_with_read_factory()
Factory method to generate a CollisionSphere object.
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
A class to retrieve the individual data elements previously stored in a Datagram.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:133
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
Defines a series of triangle strips.
Definition: geomTristrips.h:23
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition: geom.h:54
A lightweight class that represents a single element that may be timed and/or counted via stats.
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(CollisionEntry) CollisionSphere
Transforms the solid by the indicated matrix.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.