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  */
81 LPoint3 CollisionSphere::
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  nassertr(A != 0.0, false);
624 
625  LVector3 fc = from - get_center();
626  double B = 2.0f* dot(delta, fc);
627  double fc_d2 = dot(fc, fc);
628  double radius = get_radius() + inflate_radius;
629  double C = fc_d2 - radius * radius;
630 
631  double radical = B*B - 4.0*A*C;
632 
633  if (IS_NEARLY_ZERO(radical)) {
634  // Tangent.
635  t1 = t2 = -B /(2.0*A);
636  return true;
637 
638  } else if (radical < 0.0) {
639  // No real roots: no intersection with the line.
640  return false;
641  }
642 
643  double reciprocal_2A = 1.0/(2.0*A);
644  double sqrt_radical = sqrtf(radical);
645  t1 = ( -B - sqrt_radical ) * reciprocal_2A;
646  t2 = ( -B + sqrt_radical ) * reciprocal_2A;
647 
648  return true;
649 }
650 
651 /**
652  * Determine a point of intersection of a parametric parabola with the sphere.
653  *
654  * We only consider the segment of the parabola between t1 and t2, which has
655  * already been computed as corresponding to points p1 and p2. If there is an
656  * intersection, t is set to the parametric point of intersection, and true is
657  * returned; otherwise, false is returned.
658  */
659 bool CollisionSphere::
660 intersects_parabola(double &t, const LParabola &parabola,
661  double t1, double t2,
662  const LPoint3 &p1, const LPoint3 &p2) const {
663  if (t1 == t2) {
664  // Special case: a single point.
665  if ((p1 - _center).length_squared() > _radius * _radius) {
666  // No intersection.
667  return false;
668  }
669  t = t1;
670  return true;
671  }
672 
673  // To directly test for intersection between a parabola (quadratic) and a
674  // sphere (also quadratic) requires solving a quartic equation. Doable, but
675  // hard, and I'm a programmer, not a mathematician. So I'll solve it the
676  // programmer's way instead, by approximating the parabola with a series of
677  // line segments. Hence, this function works by recursively subdividing the
678  // parabola as necessary.
679 
680  // First, see if the line segment (p1 - p2) comes sufficiently close to the
681  // parabola. Do this by computing the parametric intervening point and
682  // comparing its distance from the linear intervening point.
683  double tmid = (t1 + t2) * 0.5;
684  if (tmid != t1 && tmid != t2) {
685  LPoint3 pmid = parabola.calc_point(tmid);
686  LPoint3 pmid2 = (p1 + p2) * 0.5f;
687 
688  if ((pmid - pmid2).length_squared() > 0.001f) {
689  // Subdivide.
690  if (intersects_parabola(t, parabola, t1, tmid, p1, pmid)) {
691  return true;
692  }
693  return intersects_parabola(t, parabola, tmid, t2, pmid, p2);
694  }
695  }
696 
697  // The line segment is sufficiently close; compare the segment itself.
698  double t1a, t2a;
699  if (!intersects_line(t1a, t2a, p1, p2 - p1, 0.0f)) {
700  return false;
701  }
702 
703  if (t2a < 0.0 || t1a > 1.0) {
704  return false;
705  }
706 
707  t = max(t1a, 0.0);
708  return true;
709 }
710 
711 /**
712  * Returns a point on the surface of the sphere. latitude and longitude range
713  * from 0.0 to 1.0. This is used by fill_viz_geom() to create a visible
714  * representation of the sphere.
715  */
716 LVertex CollisionSphere::
717 compute_point(PN_stdfloat latitude, PN_stdfloat longitude) const {
718  PN_stdfloat s1, c1;
719  csincos(latitude * MathNumbers::pi, &s1, &c1);
720 
721  PN_stdfloat s2, c2;
722  csincos(longitude * 2.0f * MathNumbers::pi, &s2, &c2);
723 
724  LVertex p(s1 * c2, s1 * s2, c1);
725  return p * get_radius() + get_center();
726 }
727 
728 /**
729  * Factory method to generate a CollisionSphere object
730  */
733  BamReader::get_factory()->register_factory(get_class_type(), make_CollisionSphere);
734 }
735 
736 /**
737  * Function to write the important information in the particular object to a
738  * Datagram
739  */
742  CollisionSolid::write_datagram(manager, me);
743  _center.write_datagram(me);
744  me.add_stdfloat(_radius);
745 }
746 
747 /**
748  * Factory method to generate a CollisionSphere object
749  */
750 TypedWritable *CollisionSphere::
751 make_CollisionSphere(const FactoryParams &params) {
753  DatagramIterator scan;
754  BamReader *manager;
755 
756  parse_params(params, scan, manager);
757  me->fillin(scan, manager);
758  return me;
759 }
760 
761 /**
762  * Function that reads out of the datagram (or asks manager to read) all of
763  * the data that is needed to re-create this object and stores it in the
764  * appropiate place
765  */
766 void CollisionSphere::
767 fillin(DatagramIterator& scan, BamReader* manager) {
768  CollisionSolid::fillin(scan, manager);
769  _center.read_datagram(scan);
770  _radius = scan.get_stdfloat();
771 }
get_t2
Returns the ending point on the parabola.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
An infinite ray, with a specific origin and direction.
Definition: collisionRay.h:27
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" 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:110
static void register_with_read_factory()
Factory method to generate a CollisionSphere object.
The abstract base class for all things that can collide with other things in the world,...
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
This defines a bounding sphere, consisting of a center and a radius.
A cuboid collision volume or object.
Definition: collisionBox.h:27
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
get_parabola
Returns the parabola specified by this solid.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Defines a series of triangle strips.
Definition: geomTristrips.h:23
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
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.
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
get_respect_effective_normal
See set_respect_effective_normal().
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
get_t1
Returns the starting point on the parabola.
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.
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
A finite line segment, with two specific endpoints but no thickness.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Defines a single collision event.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This implements a solid consisting of a cylinder with hemispherical endcaps, also known as a capsule ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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.
An infinite line, similar to a CollisionRay, except that it extends in both directions.
Definition: collisionLine.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
get_into
Returns the CollisionSolid pointer for the particular solid was collided into.
get_from
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
This defines a parabolic arc, or subset of an arc, similar to the path of a projectile or falling obj...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.