Panda3D
Loading...
Searching...
No Matches
collisionBox.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 collisionBox.cxx
10 * @author amith tudur
11 * @date 2009-07-31
12 */
13
14#include "collisionBox.h"
15#include "collisionLine.h"
16#include "collisionRay.h"
17#include "collisionSphere.h"
18#include "collisionSegment.h"
19#include "collisionCapsule.h"
20#include "collisionHandler.h"
21#include "collisionEntry.h"
22#include "config_collide.h"
23#include "boundingSphere.h"
24#include "datagram.h"
25#include "datagramIterator.h"
26#include "bamReader.h"
27#include "bamWriter.h"
28#include "nearly_zero.h"
29#include "cmath.h"
30#include "mathNumbers.h"
31#include "geom.h"
32#include "geomLines.h"
33#include "geomTriangles.h"
34#include "geomVertexWriter.h"
35#include "config_mathutil.h"
36#include "dcast.h"
37
38#include <math.h>
39
40using std::max;
41using std::min;
42
43PStatCollector CollisionBox::_volume_pcollector("Collision Volumes:CollisionBox");
44PStatCollector CollisionBox::_test_pcollector("Collision Tests:CollisionBox");
45TypeHandle CollisionBox::_type_handle;
46
47const int CollisionBox::plane_def[6][4] = {
48 {0, 4, 5, 1},
49 {4, 6, 7, 5},
50 {6, 2, 3, 7},
51 {2, 0, 1, 3},
52 {1, 5, 7, 3},
53 {2, 6, 4, 0},
54};
55
56/**
57 *
58 */
59CollisionSolid *CollisionBox::
60make_copy() {
61 return new CollisionBox(*this);
62}
63
64/**
65 * Compute parameters for each of the box's sides
66 */
68setup_box(){
69 for(int plane = 0; plane < 6; plane++) {
70 LPoint3 array[4];
71 array[0] = get_point(plane_def[plane][0]);
72 array[1] = get_point(plane_def[plane][1]);
73 array[2] = get_point(plane_def[plane][2]);
74 array[3] = get_point(plane_def[plane][3]);
75 setup_points(array, array+4, plane);
76 }
77}
78
79/**
80 * Computes the plane and 2d projection of points that make up this side.
81 */
83setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
84 int num_points = end - begin;
85 nassertv(num_points >= 3);
86
87 _points[plane].clear();
88
89 // Construct a matrix that rotates the points from the (X,0,Z) plane into
90 // the 3-d plane.
91 LMatrix4 to_3d_mat;
92 calc_to_3d_mat(to_3d_mat, plane);
93
94 // And the inverse matrix rotates points from 3-d space into the 2-d plane.
95 _to_2d_mat[plane].invert_from(to_3d_mat);
96
97 // Now project all of the points onto the 2-d plane.
98
99 const LPoint3 *pi;
100 for (pi = begin; pi != end; ++pi) {
101 LPoint3 point = (*pi) * _to_2d_mat[plane];
102 _points[plane].push_back(PointDef(point[0], point[2]));
103 }
104
105 nassertv(_points[plane].size() >= 3);
106
107#ifndef NDEBUG
108 /*
109 // Now make sure the points define a convex polygon.
110 if (is_concave()) {
111 collide_cat.error() << "Invalid concave CollisionPolygon defined:\n";
112 const LPoint3 *pi;
113 for (pi = begin; pi != end; ++pi) {
114 collide_cat.error(false) << " " << (*pi) << "\n";
115 }
116 collide_cat.error(false)
117 << " normal " << normal << " with length " << normal.length() << "\n";
118 _points.clear();
119 }
120 */
121#endif
122
123 compute_vectors(_points[plane]);
124}
125
126/**
127 * First Dispatch point for box as a FROM object
128 */
129PT(CollisionEntry) CollisionBox::
130test_intersection(const CollisionEntry &entry) const {
131 return entry.get_into()->test_intersection_from_box(entry);
132}
133
134/**
135 * Transforms the solid by the indicated matrix.
136 */
137void CollisionBox::
138xform(const LMatrix4 &mat) {
139 _min = _min * mat;
140 _max = _max * mat;
141 _center = _center * mat;
142 for(int v = 0; v < 8; v++) {
143 _vertex[v] = _vertex[v] * mat;
144 }
145 for(int p = 0; p < 6 ; p++) {
146 _planes[p] = set_plane(p);
147 }
148 _x = _vertex[0].get_x() - _center.get_x();
149 _y = _vertex[0].get_y() - _center.get_y();
150 _z = _vertex[0].get_z() - _center.get_z();
151 _radius = sqrt(_x * _x + _y * _y + _z * _z);
152 setup_box();
153 mark_viz_stale();
154 mark_internal_bounds_stale();
155}
156
157/**
158 * Returns the point in space deemed to be the "origin" of the solid for
159 * collision purposes. The closest intersection point to this origin point is
160 * considered to be the most significant.
161 */
163get_collision_origin() const {
164 return _center;
165}
166
167/**
168 * Returns a PStatCollector that is used to count the number of bounding
169 * volume tests made against a solid of this type in a given frame.
170 */
173 return _volume_pcollector;
174}
175
176/**
177 * Returns a PStatCollector that is used to count the number of intersection
178 * tests made against a solid of this type in a given frame.
179 */
182 return _test_pcollector;
183}
184
185/**
186 *
187 */
188void CollisionBox::
189output(std::ostream &out) const {
190 out << "box, (" << get_min() << ") to (" << get_max() << ")";
191}
192
193/**
194 * Sphere is chosen as the Bounding Volume type for speed and efficiency
195 */
196PT(BoundingVolume) CollisionBox::
197compute_internal_bounds() const {
198 return new BoundingSphere(_center, _radius);
199}
200
201/**
202 * Double dispatch point for sphere as FROM object
203 */
204PT(CollisionEntry) CollisionBox::
205test_intersection_from_sphere(const CollisionEntry &entry) const {
206
207 const CollisionSphere *sphere;
208 DCAST_INTO_R(sphere, entry.get_from(), nullptr);
209
210 CPT(TransformState) wrt_space = entry.get_wrt_space();
211 CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
212
213 const LMatrix4 &wrt_mat = wrt_space->get_mat();
214
215 LPoint3 orig_center = sphere->get_center() * wrt_mat;
216 LPoint3 from_center = orig_center;
217 bool moved_from_center = false;
218 PN_stdfloat t = 1.0f;
219 LPoint3 contact_point(from_center);
220 PN_stdfloat actual_t = 1.0f;
221
222 LVector3 from_radius_v =
223 LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
224 PN_stdfloat from_radius_2 = from_radius_v.length_squared();
225 PN_stdfloat from_radius = csqrt(from_radius_2);
226
227 int ip;
228 PN_stdfloat max_dist = 0.0;
229 PN_stdfloat dist = 0.0;
230 bool intersect;
231 LPlane plane;
232 LVector3 normal;
233 bool fully_inside = true;
234
235 for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) {
236 plane = get_plane( ip );
237 if (_points[ip].size() < 3) {
238 fully_inside = false;
239 continue;
240 }
241 if (wrt_prev_space != wrt_space) {
242 // If we have a delta between the previous position and the current
243 // position, we use that to determine some more properties of the
244 // collision.
245 LPoint3 b = from_center;
246 LPoint3 a = sphere->get_center() * wrt_prev_space->get_mat();
247 LVector3 delta = b - a;
248
249 // First, there is no collision if the "from" object is definitely
250 // moving in the same direction as the plane's normal.
251 PN_stdfloat dot = delta.dot(plane.get_normal());
252 if (dot > 0.1f) {
253 fully_inside = false;
254 continue; // no intersection
255 }
256
257 if (IS_NEARLY_ZERO(dot)) {
258 // If we're moving parallel to the plane, the sphere is tested at its
259 // final point. Leave it as it is.
260
261 } else {
262/*
263 * Otherwise, we're moving into the plane; the sphere is tested at the point
264 * along its path that is closest to intersecting the plane. This may be the
265 * actual intersection point, or it may be the starting point or the final
266 * point. dot is equal to the (negative) magnitude of 'delta' along the
267 * direction of the plane normal t = ratio of (distance from start pos to
268 * plane) to (distance from start pos to end pos), along axis of plane normal
269 */
270 PN_stdfloat dist_to_p = plane.dist_to_plane(a);
271 t = (dist_to_p / -dot);
272
273 // also compute the actual contact point and time of contact for
274 // handlers that need it
275 actual_t = ((dist_to_p - from_radius) / -dot);
276 actual_t = min((PN_stdfloat)1.0, max((PN_stdfloat)0.0, actual_t));
277 contact_point = a + (actual_t * delta);
278
279 if (t >= 1.0f) {
280 // Leave it where it is.
281
282 } else if (t < 0.0f) {
283 from_center = a;
284 moved_from_center = true;
285 } else {
286 from_center = a + t * delta;
287 moved_from_center = true;
288 }
289 }
290 }
291
292 normal = (has_effective_normal() && sphere->get_respect_effective_normal()) ? get_effective_normal() : plane.get_normal();
293
294#ifndef NDEBUG
295 /*if (!IS_THRESHOLD_EQUAL(normal.length_squared(), 1.0f, 0.001), NULL) {
296 std::cout
297 << "polygon within " << entry.get_into_node_path()
298 << " has normal " << normal << " of length " << normal.length()
299 << "\n";
300 normal.normalize();
301 }*/
302#endif
303
304 // The nearest point within the plane to our center is the intersection of
305 // the line (center, center - normal) with the plane.
306
307 if (!plane.intersects_line(dist, from_center, -(plane.get_normal()))) {
308 // No intersection with plane? This means the plane's effective normal
309 // was within the plane itself. A useless polygon.
310 fully_inside = false;
311 continue;
312 }
313
314 if (dist > from_radius) {
315 // Fully outside this plane, there can not be an intersection.
316 return nullptr;
317 }
318 if (dist < -from_radius) {
319 // Fully inside this plane.
320 continue;
321 }
322 fully_inside = false;
323
324 LPoint2 p = to_2d(from_center - dist * plane.get_normal(), ip);
325 PN_stdfloat edge_dist = 0.0f;
326
327 const ClipPlaneAttrib *cpa = entry.get_into_clip_planes();
328 if (cpa != nullptr) {
329 // We have a clip plane; apply it.
330 Points new_points;
331 if (apply_clip_plane(new_points, cpa, entry.get_into_node_path().get_net_transform(),ip)) {
332 // All points are behind the clip plane; just do the default test.
333 edge_dist = dist_to_polygon(p, _points[ip]);
334 } else if (new_points.empty()) {
335 // The polygon is completely clipped.
336 continue;
337 } else {
338 // Test against the clipped polygon.
339 edge_dist = dist_to_polygon(p, new_points);
340 }
341 } else {
342 // No clip plane is in effect. Do the default test.
343 edge_dist = dist_to_polygon(p, _points[ip]);
344 }
345
346 max_dist = from_radius;
347
348 // Now we have edge_dist, which is the distance from the sphere center to
349 // the nearest edge of the polygon, within the polygon's plane.
350 // edge_dist<0 means the point is within the polygon.
351 if(edge_dist < 0) {
352 intersect = true;
353 continue;
354 }
355
356 if((edge_dist > 0) &&
357 ((edge_dist * edge_dist + dist * dist) > from_radius_2)) {
358 // No intersection; the circle is outside the polygon.
359 continue;
360 }
361
362 // The sphere appears to intersect the polygon. If the edge is less than
363 // from_radius away, the sphere may be resting on an edge of the polygon.
364 // Determine how far the center of the sphere must remain from the plane,
365 // based on its distance from the nearest edge.
366
367 if (edge_dist >= 0.0f) {
368 PN_stdfloat max_dist_2 = max(from_radius_2 - edge_dist * edge_dist, (PN_stdfloat)0.0);
369 max_dist = csqrt(max_dist_2);
370 }
371
372 if (dist > max_dist) {
373 // There's no intersection: the sphere is hanging off the edge.
374 continue;
375 }
376 intersect = true;
377 }
378 if (!fully_inside && !intersect) {
379 return nullptr;
380 }
381
382 if (collide_cat.is_debug()) {
383 collide_cat.debug()
384 << "intersection detected from " << entry.get_from_node_path()
385 << " into " << entry.get_into_node_path() << "\n";
386 }
387
388 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
389
390 PN_stdfloat into_depth = max_dist - dist;
391 if (moved_from_center) {
392 // We have to base the depth of intersection on the sphere's final resting
393 // point, not the point from which we tested the intersection.
394 PN_stdfloat orig_dist;
395 plane.intersects_line(orig_dist, orig_center, -normal);
396 into_depth = max_dist - orig_dist;
397 }
398
399 // Clamp the surface point to the box bounds.
400 LPoint3 surface = from_center - normal * dist;
401 surface = surface.fmax(_min);
402 surface = surface.fmin(_max);
403
404 new_entry->set_surface_normal(normal);
405 new_entry->set_surface_point(surface);
406 new_entry->set_interior_point(surface - normal * into_depth);
407 new_entry->set_contact_pos(contact_point);
408 new_entry->set_contact_normal(plane.get_normal());
409 new_entry->set_t(actual_t);
410
411 return new_entry;
412}
413
414/**
415 *
416 */
417PT(CollisionEntry) CollisionBox::
418test_intersection_from_line(const CollisionEntry &entry) const {
419 const CollisionLine *line;
420 DCAST_INTO_R(line, entry.get_from(), nullptr);
421
422 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
423
424 LPoint3 from_origin = line->get_origin() * wrt_mat;
425 LVector3 from_direction = line->get_direction() * wrt_mat;
426
427 double t1, t2;
428 if (!intersects_line(t1, t2, from_origin, from_direction)) {
429 // No intersection.
430 return nullptr;
431 }
432
433 if (collide_cat.is_debug()) {
434 collide_cat.debug()
435 << "intersection detected from " << entry.get_from_node_path()
436 << " into " << entry.get_into_node_path() << "\n";
437 }
438 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
439
440 LPoint3 point = from_origin + t1 * from_direction;
441 new_entry->set_surface_point(point);
442
444 new_entry->set_surface_normal(get_effective_normal());
445 } else {
446 LVector3 normal(
447 IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
448 IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
449 IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
450 );
451 normal.normalize();
452 new_entry->set_surface_normal(normal);
453 }
454
455 return new_entry;
456}
457
458/**
459 * Double dispatch point for ray as a FROM object
460 */
461PT(CollisionEntry) CollisionBox::
462test_intersection_from_ray(const CollisionEntry &entry) const {
463 const CollisionRay *ray;
464 DCAST_INTO_R(ray, entry.get_from(), nullptr);
465 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
466
467 LPoint3 from_origin = ray->get_origin() * wrt_mat;
468 LVector3 from_direction = ray->get_direction() * wrt_mat;
469
470 double t1, t2;
471 if (!intersects_line(t1, t2, from_origin, from_direction) || (t1 < 0.0 && t2 < 0.0)) {
472 // No intersection.
473 return nullptr;
474 }
475
476 if (collide_cat.is_debug()) {
477 collide_cat.debug()
478 << "intersection detected from " << entry.get_from_node_path()
479 << " into " << entry.get_into_node_path() << "\n";
480 }
481 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
482
483 if (t1 < 0.0) {
484 // The origin is inside the box, so we take the exit as our surface point.
485 new_entry->set_interior_point(from_origin);
486 t1 = t2;
487 }
488
489 LPoint3 point = from_origin + t1 * from_direction;
490 new_entry->set_surface_point(point);
491
493 new_entry->set_surface_normal(get_effective_normal());
494 } else {
495 LVector3 normal(
496 IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
497 IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
498 IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
499 );
500 normal.normalize();
501 new_entry->set_surface_normal(normal);
502 }
503
504 return new_entry;
505}
506
507/**
508 * Double dispatch point for segment as a FROM object
509 */
510PT(CollisionEntry) CollisionBox::
511test_intersection_from_segment(const CollisionEntry &entry) const {
512 const CollisionSegment *seg;
513 DCAST_INTO_R(seg, entry.get_from(), nullptr);
514 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
515
516 LPoint3 from_origin = seg->get_point_a() * wrt_mat;
517 LPoint3 from_extent = seg->get_point_b() * wrt_mat;
518 LVector3 from_direction = from_extent - from_origin;
519
520 double t1, t2;
521 if (!intersects_line(t1, t2, from_origin, from_direction) ||
522 (t1 < 0.0 && t2 < 0.0) || (t1 > 1.0 && t2 > 1.0)) {
523 // No intersection.
524 return nullptr;
525 }
526
527 if (collide_cat.is_debug()) {
528 collide_cat.debug()
529 << "intersection detected from " << entry.get_from_node_path()
530 << " into " << entry.get_into_node_path() << "\n";
531 }
532 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
533
534 // In case the segment is entirely inside the cube, we consider the point
535 // closest to the surface as our entry point.
536 if (t1 < (1.0 - t2)) {
537 std::swap(t1, t2);
538 }
539
540 // Our interior point is the closest point to t2 that is inside the segment.
541 new_entry->set_interior_point(from_origin + std::min(std::max(t2, 0.0), 1.0) * from_direction);
542
543 LPoint3 point = from_origin + t1 * from_direction;
544 new_entry->set_surface_point(point);
545
547 new_entry->set_surface_normal(get_effective_normal());
548 } else {
549 LVector3 normal(
550 IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
551 IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
552 IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
553 );
554 normal.normalize();
555 new_entry->set_surface_normal(normal);
556 }
557
558 return new_entry;
559}
560
561/**
562 * Double dispatch point for capsule as a FROM object
563 */
564PT(CollisionEntry) CollisionBox::
565test_intersection_from_capsule(const CollisionEntry &entry) const {
566 const CollisionCapsule *capsule;
567 DCAST_INTO_R(capsule, entry.get_from(), nullptr);
568
569 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
570
571 LPoint3 from_a = capsule->get_point_a() * wrt_mat;
572 LPoint3 from_b = capsule->get_point_b() * wrt_mat;
573 LVector3 from_direction = from_b - from_a;
574 PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, capsule->get_radius())).length_squared();
575 PN_stdfloat radius = csqrt(radius_sq);
576
577 LPoint3 box_min = get_min();
578 LPoint3 box_max = get_max();
579 LVector3 dimensions = box_max - box_min;
580
581 // The method below is inspired by Christer Ericson's book Real-Time
582 // Collision Detection. Instead of testing a capsule against a box, we test
583 // a segment against an box that is oversized by the capsule radius.
584
585 // First, we test if the line segment intersects a box with its faces
586 // expanded outwards by the capsule radius. If not, there is no collision.
587 double t1, t2;
588 if (!intersects_line(t1, t2, from_a, from_direction, radius)) {
589 return nullptr;
590 }
591
592 if (t2 < 0.0 || t1 > 1.0) {
593 return nullptr;
594 }
595
596 t1 = std::min(1.0, std::max(0.0, (t1 + t2) * 0.5));
597 LPoint3 point = from_a + from_direction * t1;
598
599 // We now have a point of intersection between the line segment and the
600 // oversized box. Check on how many axes it lies outside the box. If it is
601 // less than two, we know that it does not lie in one of the rounded regions
602 // of the oversized rounded box, and it is a guaranteed hit. Otherwise, we
603 // will need to test against the edge regions.
604 if ((point[0] < box_min[0] || point[0] > box_max[0]) +
605 (point[1] < box_min[1] || point[1] > box_max[1]) +
606 (point[2] < box_min[2] || point[2] > box_max[2]) > 1) {
607 // Test the capsule against each edge of the box.
608 static const struct {
609 LPoint3 point;
610 int axis;
611 } edges[] = {
612 {{0, 0, 0}, 0},
613 {{0, 1, 0}, 0},
614 {{0, 0, 1}, 0},
615 {{0, 1, 1}, 0},
616 {{0, 0, 0}, 1},
617 {{0, 0, 1}, 1},
618 {{1, 0, 0}, 1},
619 {{1, 0, 1}, 1},
620 {{0, 0, 0}, 2},
621 {{0, 1, 0}, 2},
622 {{1, 0, 0}, 2},
623 {{1, 1, 0}, 2},
624 };
625
626 PN_stdfloat best_dist_sq = FLT_MAX;
627
628 for (int i = 0; i < 12; ++i) {
629 LPoint3 vertex = edges[i].point;
630 vertex.componentwise_mult(dimensions);
631 vertex += box_min;
632 LVector3 delta(0);
633 delta[edges[i].axis] = dimensions[edges[i].axis];
634 double u1, u2;
635 CollisionCapsule::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta);
636 PN_stdfloat dist_sq = ((from_a + from_direction * u1) - (vertex + delta * u2)).length_squared();
637 if (dist_sq < best_dist_sq) {
638 best_dist_sq = dist_sq;
639 }
640 }
641
642 if (best_dist_sq > radius_sq) {
643 // It is not actually touching any edge.
644 return nullptr;
645 }
646 }
647
648 if (collide_cat.is_debug()) {
649 collide_cat.debug()
650 << "intersection detected from " << entry.get_from_node_path()
651 << " into " << entry.get_into_node_path() << "\n";
652 }
653 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
654
655 // Which is the longest axis?
656 LVector3 diff = point - _center;
657 diff[0] /= dimensions[0];
658 diff[1] /= dimensions[1];
659 diff[2] /= dimensions[2];
660 int axis = 0;
661 if (cabs(diff[0]) > cabs(diff[1])) {
662 if (cabs(diff[0]) > cabs(diff[2])) {
663 axis = 0;
664 } else {
665 axis = 2;
666 }
667 } else {
668 if (cabs(diff[1]) > cabs(diff[2])) {
669 axis = 1;
670 } else {
671 axis = 2;
672 }
673 }
674 LVector3 normal(0);
675 normal[axis] = std::copysign(1, diff[axis]);
676
677 LPoint3 clamped = point.fmax(box_min).fmin(box_max);
678 LPoint3 surface_point = clamped;
679 surface_point[axis] = (diff[axis] >= 0.0f) ? box_max[axis] : box_min[axis];
680
681 // Is the point inside the box?
682 LVector3 interior_vec;
683 if (clamped != point) {
684 // No, it is outside. The interior point is in the direction of the
685 // surface point.
686 interior_vec = point - surface_point;
687 if (!interior_vec.normalize()) {
688 interior_vec = normal;
689 }
690 } else {
691 // It is inside. I think any point will work for this.
692 interior_vec = normal;
693 }
694 new_entry->set_interior_point(point - interior_vec * radius);
695 new_entry->set_surface_point(surface_point);
696
698 new_entry->set_surface_normal(get_effective_normal());
699 } else {
700 new_entry->set_surface_normal(normal);
701 }
702
703 return new_entry;
704}
705
706/**
707 * Double dispatch point for box as a FROM object
708 */
709PT(CollisionEntry) CollisionBox::
710test_intersection_from_box(const CollisionEntry &entry) const {
711 const CollisionBox *box;
712 DCAST_INTO_R(box, entry.get_from(), nullptr);
713
714 const LMatrix4 &wrt_mat = entry.get_wrt_mat();
715
716 LPoint3 diff = wrt_mat.xform_point_general(box->get_center()) - _center;
717 LVector3 from_extents = box->get_dimensions() * 0.5f;
718 LVector3 into_extents = get_dimensions() * 0.5f;
719
720 LVecBase3 box_x = wrt_mat.get_row3(0);
721 LVecBase3 box_y = wrt_mat.get_row3(1);
722 LVecBase3 box_z = wrt_mat.get_row3(2);
723
724 // To make the math simpler, normalize the box basis vectors, instead
725 // applying the scale to the box dimensions. Note that this doesn't work
726 // for a non-uniform scales applied after a rotation, since that has the
727 // possibility of making the box no longer a box.
728 PN_stdfloat l;
729 l = box_x.length();
730 from_extents[0] *= l;
731 box_x /= l;
732 l = box_y.length();
733 from_extents[1] *= l;
734 box_y /= l;
735 l = box_z.length();
736 from_extents[2] *= l;
737 box_z /= l;
738
739 PN_stdfloat r1, r2;
740 PN_stdfloat min_pen = 0;
741 PN_stdfloat pen;
742 int axis = 0;
743
744 // SAT test for the three axes of the into cube.
745 r1 = into_extents[0];
746 r2 = cabs(box_x[0] * from_extents[0]) +
747 cabs(box_y[0] * from_extents[1]) +
748 cabs(box_z[0] * from_extents[2]);
749 pen = r1 + r2 - cabs(diff[0]);
750 if (pen < 0) {
751 return nullptr;
752 }
753 min_pen = pen;
754
755 r1 = into_extents[1];
756 r2 = cabs(box_x[1] * from_extents[0]) +
757 cabs(box_y[1] * from_extents[1]) +
758 cabs(box_z[1] * from_extents[2]);
759 pen = r1 + r2 - cabs(diff[1]);
760 if (pen < 0) {
761 return nullptr;
762 }
763 if (pen < min_pen) {
764 min_pen = pen;
765 axis = 1;
766 }
767
768 r1 = into_extents[2];
769 r2 = cabs(box_x[2] * from_extents[0]) +
770 cabs(box_y[2] * from_extents[1]) +
771 cabs(box_z[2] * from_extents[2]);
772 pen = r1 + r2 - cabs(diff[2]);
773 if (pen < 0) {
774 return nullptr;
775 }
776 if (pen < min_pen) {
777 min_pen = pen;
778 axis = 2;
779 }
780
781 // SAT test for the three axes of the from cube.
782 r1 = cabs(box_x[0] * into_extents[0]) +
783 cabs(box_x[1] * into_extents[1]) +
784 cabs(box_x[2] * into_extents[2]);
785 r2 = from_extents[0];
786 pen = r1 + r2 - cabs(diff.dot(box_x));
787 if (pen < 0) {
788 return nullptr;
789 }
790 if (pen < min_pen) {
791 min_pen = pen;
792 }
793
794 r1 = cabs(box_y[0] * into_extents[0]) +
795 cabs(box_y[1] * into_extents[1]) +
796 cabs(box_y[2] * into_extents[2]);
797 r2 = from_extents[1];
798 pen = r1 + r2 - cabs(diff.dot(box_y));
799 if (pen < 0) {
800 return nullptr;
801 }
802 if (pen < min_pen) {
803 min_pen = pen;
804 }
805
806 r1 = cabs(box_z[0] * into_extents[0]) +
807 cabs(box_z[1] * into_extents[1]) +
808 cabs(box_z[2] * into_extents[2]);
809 r2 = from_extents[2];
810 pen = r1 + r2 - cabs(diff.dot(box_z));
811 if (pen < 0) {
812 return nullptr;
813 }
814 if (pen < min_pen) {
815 min_pen = pen;
816 }
817
818 // SAT test of the nine cross products.
819 r1 = into_extents[1] * cabs(box_x[2]) + into_extents[2] * cabs(box_x[1]);
820 r2 = from_extents[1] * cabs(box_z[0]) + from_extents[2] * cabs(box_y[0]);
821 if (cabs(diff[2] * box_x[1] - diff[1] * box_x[2]) > r1 + r2) {
822 return nullptr;
823 }
824
825 r1 = into_extents[1] * cabs(box_y[2]) + into_extents[2] * cabs(box_y[1]);
826 r2 = from_extents[0] * cabs(box_z[0]) + from_extents[2] * cabs(box_x[0]);
827 if (cabs(diff[2] * box_y[1] - diff[1] * box_y[2]) > r1 + r2) {
828 return nullptr;
829 }
830
831 r1 = into_extents[1] * cabs(box_z[2]) + into_extents[2] * cabs(box_z[1]);
832 r2 = from_extents[0] * cabs(box_y[0]) + from_extents[1] * cabs(box_x[0]);
833 if (cabs(diff[2] * box_z[1] - diff[1] * box_z[2]) > r1 + r2) {
834 return nullptr;
835 }
836
837 r1 = into_extents[0] * cabs(box_x[2]) + into_extents[2] * cabs(box_x[0]);
838 r2 = from_extents[1] * cabs(box_z[1]) + from_extents[2] * cabs(box_y[1]);
839 if (cabs(diff[0] * box_x[2] - diff[2] * box_x[0]) > r1 + r2) {
840 return nullptr;
841 }
842
843 r1 = into_extents[0] * cabs(box_y[2]) + into_extents[2] * cabs(box_y[0]);
844 r2 = from_extents[0] * cabs(box_z[1]) + from_extents[2] * cabs(box_x[1]);
845 if (cabs(diff[0] * box_y[2] - diff[2] * box_y[0]) > r1 + r2) {
846 return nullptr;
847 }
848
849 r1 = into_extents[0] * cabs(box_z[2]) + into_extents[2] * cabs(box_z[0]);
850 r2 = from_extents[0] * cabs(box_y[1]) + from_extents[1] * cabs(box_x[1]);
851 if (cabs(diff[0] * box_z[2] - diff[2] * box_z[0]) > r1 + r2) {
852 return nullptr;
853 }
854
855 r1 = into_extents[0] * cabs(box_x[1]) + into_extents[1] * cabs(box_x[0]);
856 r2 = from_extents[1] * cabs(box_z[2]) + from_extents[2] * cabs(box_y[2]);
857 if (cabs(diff[1] * box_x[0] - diff[0] * box_x[1]) > r1 + r2) {
858 return nullptr;
859 }
860
861 r1 = into_extents[0] * cabs(box_y[1]) + into_extents[1] * cabs(box_y[0]);
862 r2 = from_extents[0] * cabs(box_z[2]) + from_extents[2] * cabs(box_x[2]);
863 if (cabs(diff[1] * box_y[0] - diff[0] * box_y[1]) > r1 + r2) {
864 return nullptr;
865 }
866
867 r1 = into_extents[0] * cabs(box_z[1]) + into_extents[1] * cabs(box_z[0]);
868 r2 = from_extents[0] * cabs(box_y[2]) + from_extents[1] * cabs(box_x[2]);
869 if (cabs(diff[1] * box_z[0] - diff[0] * box_z[1]) > r1 + r2) {
870 return nullptr;
871 }
872
873 if (collide_cat.is_debug()) {
874 collide_cat.debug()
875 << "intersection detected from " << entry.get_from_node_path()
876 << " into " << entry.get_into_node_path() << "\n";
877 }
878 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
879
880 // This isn't always the correct surface point. However, it seems to be
881 // enough to let the pusher do the right thing.
882 LPoint3 surface(
883 min(max(diff[0], -into_extents[0]), into_extents[0]),
884 min(max(diff[1], -into_extents[1]), into_extents[1]),
885 min(max(diff[2], -into_extents[2]), into_extents[2]));
886
887 // Create the normal along the axis of least penetration.
888 LVector3 normal(0);
889 PN_stdfloat diff_axis = diff[axis];
890 int sign = (diff_axis >= 0) ? 1 : -1;
891 normal[axis] = sign;
892 surface[axis] = into_extents[axis] * sign;
893
894 new_entry->set_surface_point(surface + _center);
895
896 // Does not generate the correct depth. Needs fixing.
897 new_entry->set_interior_point(surface + _center + normal * -min_pen);
898
900 new_entry->set_surface_normal(get_effective_normal());
901 } else {
902 new_entry->set_surface_normal(normal);
903 }
904
905 return new_entry;
906}
907
908/**
909 * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
910 * solid.
911 */
912void CollisionBox::
913fill_viz_geom() {
914 if (collide_cat.is_debug()) {
915 collide_cat.debug()
916 << "Recomputing viz for " << *this << "\n";
917 }
918
919 PT(GeomVertexData) vdata = new GeomVertexData
920 ("collision", GeomVertexFormat::get_v3(),
921 Geom::UH_static);
922
923 vdata->unclean_set_num_rows(8);
924
925 {
926 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
927 vertex.set_data3(_min[0], _min[1], _min[2]);
928 vertex.set_data3(_min[0], _max[1], _min[2]);
929 vertex.set_data3(_max[0], _max[1], _min[2]);
930 vertex.set_data3(_max[0], _min[1], _min[2]);
931
932 vertex.set_data3(_min[0], _min[1], _max[2]);
933 vertex.set_data3(_min[0], _max[1], _max[2]);
934 vertex.set_data3(_max[0], _max[1], _max[2]);
935 vertex.set_data3(_max[0], _min[1], _max[2]);
936 }
937
938 PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
939
940 // Bottom
941 tris->add_vertices(0, 1, 2);
942 tris->add_vertices(2, 3, 0);
943
944 // Top
945 tris->add_vertices(4, 7, 6);
946 tris->add_vertices(6, 5, 4);
947
948 // Sides
949 tris->add_vertices(0, 4, 1);
950 tris->add_vertices(1, 4, 5);
951
952 tris->add_vertices(1, 5, 2);
953 tris->add_vertices(2, 5, 6);
954
955 tris->add_vertices(2, 6, 3);
956 tris->add_vertices(3, 6, 7);
957
958 tris->add_vertices(3, 7, 0);
959 tris->add_vertices(0, 7, 4);
960
961 PT(GeomLines) lines = new GeomLines(Geom::UH_static);
962
963 // Bottom
964 lines->add_vertices(0, 1);
965 lines->add_vertices(1, 2);
966 lines->add_vertices(0, 3);
967 lines->add_vertices(2, 3);
968
969 // Top
970 lines->add_vertices(4, 5);
971 lines->add_vertices(5, 6);
972 lines->add_vertices(4, 7);
973 lines->add_vertices(6, 7);
974
975 // Sides
976 lines->add_vertices(0, 4);
977 lines->add_vertices(1, 5);
978 lines->add_vertices(2, 6);
979 lines->add_vertices(3, 7);
980
981 PT(Geom) geom1 = new Geom(vdata);
982 geom1->add_primitive(tris);
983
984 PT(Geom) geom2 = new Geom(vdata);
985 geom2->add_primitive(lines);
986
987 _viz_geom->add_geom(geom1, get_solid_viz_state());
988 _viz_geom->add_geom(geom2, get_wireframe_viz_state());
989
990 _bounds_viz_geom->add_geom(geom1, get_solid_bounds_viz_state());
991 _bounds_viz_geom->add_geom(geom2, get_wireframe_viz_state());
992}
993
994/**
995 * Determine the point(s) of intersection of a parametric line with the box.
996 * The line is infinite in both directions, and passes through "from" and
997 * from+delta. If the line does not intersect the box, the function returns
998 * false, and t1 and t2 are undefined. If it does intersect the box, it
999 * returns true, and t1 and t2 are set to the points along the equation
1000 * from+t*delta that correspond to the two points of intersection.
1001 */
1002bool CollisionBox::
1003intersects_line(double &t1, double &t2,
1004 const LPoint3 &from, const LVector3 &delta,
1005 PN_stdfloat inflate_size) const {
1006
1007 LPoint3 bmin = _min - LVector3(inflate_size);
1008 LPoint3 bmax = _max + LVector3(inflate_size);
1009
1010 double tmin = -DBL_MAX;
1011 double tmax = DBL_MAX;
1012
1013 for (int i = 0; i < 3; ++i) {
1014 PN_stdfloat d = delta[i];
1015 if (!IS_NEARLY_ZERO(d)) {
1016 double tmin2 = (bmin[i] - from[i]) / d;
1017 double tmax2 = (bmax[i] - from[i]) / d;
1018 if (tmin2 > tmax2) {
1019 std::swap(tmin2, tmax2);
1020 }
1021 tmin = std::max(tmin, tmin2);
1022 tmax = std::min(tmax, tmax2);
1023
1024 if (tmin > tmax) {
1025 return false;
1026 }
1027
1028 } else if (from[i] < bmin[i] || from[i] > bmax[i]) {
1029 // The line is entirely parallel in this dimension.
1030 return false;
1031 }
1032 }
1033
1034 t1 = tmin;
1035 t2 = tmax;
1036 return true;
1037}
1038
1039/**
1040 * Clips the polygon by all of the clip planes named in the clip plane
1041 * attribute and fills new_points up with the resulting points.
1042 *
1043 * The return value is true if the set of points is unmodified (all points are
1044 * behind all the clip planes), or false otherwise.
1045 */
1048 const ClipPlaneAttrib *cpa,
1049 const TransformState *net_transform, int plane_no) const {
1050 bool all_in = true;
1051
1052 int num_planes = cpa->get_num_on_planes();
1053 bool first_plane = true;
1054
1055 for (int i = 0; i < num_planes; i++) {
1056 NodePath plane_path = cpa->get_on_plane(i);
1057 PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node());
1058 if ((plane_node->get_clip_effect() & PlaneNode::CE_collision) != 0) {
1059 CPT(TransformState) new_transform =
1060 net_transform->invert_compose(plane_path.get_net_transform());
1061
1062 LPlane plane = plane_node->get_plane() * new_transform->get_mat();
1063 if (first_plane) {
1064 first_plane = false;
1065 if (!clip_polygon(new_points, _points[plane_no], plane, plane_no)) {
1066 all_in = false;
1067 }
1068 } else {
1069 Points last_points;
1070 last_points.swap(new_points);
1071 if (!clip_polygon(new_points, last_points, plane, plane_no)) {
1072 all_in = false;
1073 }
1074 }
1075 }
1076 }
1077
1078 if (!all_in) {
1079 compute_vectors(new_points);
1080 }
1081
1082 return all_in;
1083}
1084/**
1085 * Clips the source_points of the polygon by the indicated clipping plane, and
1086 * modifies new_points to reflect the new set of clipped points (but does not
1087 * compute the vectors in new_points).
1088 *
1089 * The return value is true if the set of points is unmodified (all points are
1090 * behind the clip plane), or false otherwise.
1091 */
1094 const CollisionBox::Points &source_points,
1095 const LPlane &plane, int plane_no) const {
1096 new_points.clear();
1097 if (source_points.empty()) {
1098 return true;
1099 }
1100
1101 LPoint3 from3d;
1102 LVector3 delta3d;
1103 if (!plane.intersects_plane(from3d, delta3d, get_plane(plane_no))) {
1104 // The clipping plane is parallel to the polygon. The polygon is either
1105 // all in or all out.
1106 if (plane.dist_to_plane(get_plane(plane_no).get_point()) < 0.0) {
1107 // A point within the polygon is behind the clipping plane: the polygon
1108 // is all in.
1109 new_points = source_points;
1110 return true;
1111 }
1112 return false;
1113 }
1114
1115 // Project the line of intersection into the 2-d plane. Now we have a 2-d
1116 // clipping line.
1117 LPoint2 from2d = to_2d(from3d,plane_no);
1118 LVector2 delta2d = to_2d(delta3d,plane_no);
1119
1120 PN_stdfloat a = -delta2d[1];
1121 PN_stdfloat b = delta2d[0];
1122 PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
1123
1124 // Now walk through the points. Any point on the left of our line gets
1125 // removed, and the line segment clipped at the point of intersection.
1126
1127 // We might increase the number of vertices by as many as 1, if the plane
1128 // clips off exactly one corner. (We might also decrease the number of
1129 // vertices, or keep them the same number.)
1130 new_points.reserve(source_points.size() + 1);
1131
1132 LPoint2 last_point = source_points.back()._p;
1133 bool last_is_in = !is_right(last_point - from2d, delta2d);
1134 bool all_in = last_is_in;
1135 Points::const_iterator pi;
1136 for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
1137 const LPoint2 &this_point = (*pi)._p;
1138 bool this_is_in = !is_right(this_point - from2d, delta2d);
1139
1140 // There appears to be a compiler bug in gcc 4.0: we need to extract this
1141 // comparison outside of the if statement.
1142 bool crossed_over = (this_is_in != last_is_in);
1143 if (crossed_over) {
1144 // We have just crossed over the clipping line. Find the point of
1145 // intersection.
1146 LVector2 d = this_point - last_point;
1147 PN_stdfloat denom = (a * d[0] + b * d[1]);
1148 if (denom != 0.0) {
1149 PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
1150 LPoint2 p = last_point + t * d;
1151
1152 new_points.push_back(PointDef(p[0], p[1]));
1153 last_is_in = this_is_in;
1154 }
1155 }
1156
1157 if (this_is_in) {
1158 // We are behind the clipping line. Keep the point.
1159 new_points.push_back(PointDef(this_point[0], this_point[1]));
1160 } else {
1161 all_in = false;
1162 }
1163
1164 last_point = this_point;
1165 }
1166
1167 return all_in;
1168}
1169
1170
1171/**
1172 * Returns the linear distance from the 2-d point to the nearest part of the
1173 * polygon defined by the points vector. The result is negative if the point
1174 * is within the polygon.
1175 */
1177dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
1178
1179 // We know that that the polygon is convex and is defined with the points in
1180 // counterclockwise order. Therefore, we simply compare the signed distance
1181 // to each line segment; we ignore any negative values, and take the minimum
1182 // of all the positive values.
1183
1184 // If all values are negative, the point is within the polygon; we therefore
1185 // return an arbitrary negative result.
1186
1187 bool got_dist = false;
1188 PN_stdfloat best_dist = -1.0f;
1189
1190 size_t num_points = points.size();
1191 for (size_t i = 0; i < num_points - 1; ++i) {
1192 PN_stdfloat d = dist_to_line_segment(p, points[i]._p, points[i + 1]._p,
1193 points[i]._v);
1194 if (d >= 0.0f) {
1195 if (!got_dist || d < best_dist) {
1196 best_dist = d;
1197 got_dist = true;
1198 }
1199 }
1200 }
1201
1202 PN_stdfloat d = dist_to_line_segment(p, points[num_points - 1]._p, points[0]._p,
1203 points[num_points - 1]._v);
1204 if (d >= 0.0f) {
1205 if (!got_dist || d < best_dist) {
1206 best_dist = d;
1207 got_dist = true;
1208 }
1209 }
1210
1211 return best_dist;
1212}
1213
1214/**
1215 * Returns the linear distance of p to the line segment defined by f and t,
1216 * where v = (t - f).normalize(). The result is negative if p is left of the
1217 * line, positive if it is right of the line. If the result is positive, it
1218 * is constrained by endpoints of the line segment (i.e. the result might be
1219 * larger than it would be for a straight distance-to-line test). If the
1220 * result is negative, we don't bother.
1221 */
1222PN_stdfloat CollisionBox::
1223dist_to_line_segment(const LPoint2 &p,
1224 const LPoint2 &f, const LPoint2 &t,
1225 const LVector2 &v) {
1226 LVector2 v1 = (p - f);
1227 PN_stdfloat d = (v1[0] * v[1] - v1[1] * v[0]);
1228 if (d < 0.0f) {
1229 return d;
1230 }
1231
1232 // Compute the nearest point on the line.
1233 LPoint2 q = p + LVector2(-v[1], v[0]) * d;
1234
1235 // Now constrain that point to the line segment.
1236 if (v[0] > 0.0f) {
1237 // X+
1238 if (v[1] > 0.0f) {
1239 // Y+
1240 if (v[0] > v[1]) {
1241 // X-dominant.
1242 if (q[0] < f[0]) {
1243 return (p - f).length();
1244 } if (q[0] > t[0]) {
1245 return (p - t).length();
1246 } else {
1247 return d;
1248 }
1249 } else {
1250 // Y-dominant.
1251 if (q[1] < f[1]) {
1252 return (p - f).length();
1253 } if (q[1] > t[1]) {
1254 return (p - t).length();
1255 } else {
1256 return d;
1257 }
1258 }
1259 } else {
1260 // Y-
1261 if (v[0] > -v[1]) {
1262 // X-dominant.
1263 if (q[0] < f[0]) {
1264 return (p - f).length();
1265 } if (q[0] > t[0]) {
1266 return (p - t).length();
1267 } else {
1268 return d;
1269 }
1270 } else {
1271 // Y-dominant.
1272 if (q[1] > f[1]) {
1273 return (p - f).length();
1274 } if (q[1] < t[1]) {
1275 return (p - t).length();
1276 } else {
1277 return d;
1278 }
1279 }
1280 }
1281 } else {
1282 // X-
1283 if (v[1] > 0.0f) {
1284 // Y+
1285 if (-v[0] > v[1]) {
1286 // X-dominant.
1287 if (q[0] > f[0]) {
1288 return (p - f).length();
1289 } if (q[0] < t[0]) {
1290 return (p - t).length();
1291 } else {
1292 return d;
1293 }
1294 } else {
1295 // Y-dominant.
1296 if (q[1] < f[1]) {
1297 return (p - f).length();
1298 } if (q[1] > t[1]) {
1299 return (p - t).length();
1300 } else {
1301 return d;
1302 }
1303 }
1304 } else {
1305 // Y-
1306 if (-v[0] > -v[1]) {
1307 // X-dominant.
1308 if (q[0] > f[0]) {
1309 return (p - f).length();
1310 } if (q[0] < t[0]) {
1311 return (p - t).length();
1312 } else {
1313 return d;
1314 }
1315 } else {
1316 // Y-dominant.
1317 if (q[1] > f[1]) {
1318 return (p - f).length();
1319 } if (q[1] < t[1]) {
1320 return (p - t).length();
1321 } else {
1322 return d;
1323 }
1324 }
1325 }
1326 }
1327}
1328
1329/**
1330 * Returns true if the indicated point is within the polygon's 2-d space,
1331 * false otherwise.
1332 */
1334point_is_inside(const LPoint2 &p, const CollisionBox::Points &points) const {
1335 // We insist that the polygon be convex. This makes things a bit simpler.
1336 // In the case of a convex polygon, defined with points in counterclockwise
1337 // order, a point is interior to the polygon iff the point is not right of
1338 // each of the edges.
1339 for (int i = 0; i < (int)points.size() - 1; i++) {
1340 if (is_right(p - points[i]._p, points[i+1]._p - points[i]._p)) {
1341 return false;
1342 }
1343 }
1344 if (is_right(p - points[points.size() - 1]._p,
1345 points[0]._p - points[points.size() - 1]._p)) {
1346 return false;
1347 }
1348
1349 return true;
1350}
1351
1352/**
1353 * Now that the _p members of the given points array have been computed, go
1354 * back and compute all of the _v members.
1355 */
1357compute_vectors(Points &points) {
1358 size_t num_points = points.size();
1359 for (size_t i = 0; i < num_points; i++) {
1360 points[i]._v = points[(i + 1) % num_points]._p - points[i]._p;
1361 points[i]._v.normalize();
1362 }
1363}
1364
1365/**
1366 * Factory method to generate a CollisionBox object
1367 */
1370 BamReader::get_factory()->register_factory(get_class_type(), make_CollisionBox);
1371}
1372
1373/**
1374 * Function to write the important information in the particular object to a
1375 * Datagram
1376 */
1378write_datagram(BamWriter *manager, Datagram &me) {
1379 CollisionSolid::write_datagram(manager, me);
1380 _center.write_datagram(me);
1381 _min.write_datagram(me);
1382 _max.write_datagram(me);
1383 for(int i=0; i < 8; i++) {
1384 _vertex[i].write_datagram(me);
1385 }
1386 me.add_stdfloat(_radius);
1387 me.add_stdfloat(_x);
1388 me.add_stdfloat(_y);
1389 me.add_stdfloat(_z);
1390 for(int i=0; i < 6; i++) {
1391 _planes[i].write_datagram(me);
1392 }
1393 for(int i=0; i < 6; i++) {
1394 _to_2d_mat[i].write_datagram(me);
1395 }
1396 for(int i=0; i < 6; i++) {
1397 me.add_uint16(_points[i].size());
1398 for (size_t j = 0; j < _points[i].size(); j++) {
1399 _points[i][j]._p.write_datagram(me);
1400 _points[i][j]._v.write_datagram(me);
1401 }
1402 }
1403}
1404
1405/**
1406 * Factory method to generate a CollisionBox object
1407 */
1408TypedWritable *CollisionBox::
1409make_CollisionBox(const FactoryParams &params) {
1410 CollisionBox *me = new CollisionBox;
1411 DatagramIterator scan;
1412 BamReader *manager;
1413
1414 parse_params(params, scan, manager);
1415 me->fillin(scan, manager);
1416 return me;
1417}
1418
1419/**
1420 * Function that reads out of the datagram (or asks manager to read) all of
1421 * the data that is needed to re-create this object and stores it in the
1422 * appropiate place
1423 */
1424void CollisionBox::
1425fillin(DatagramIterator& scan, BamReader* manager) {
1426 CollisionSolid::fillin(scan, manager);
1427 _center.read_datagram(scan);
1428 _min.read_datagram(scan);
1429 _max.read_datagram(scan);
1430 for(int i=0; i < 8; i++) {
1431 _vertex[i].read_datagram(scan);
1432 }
1433 _radius = scan.get_stdfloat();
1434 _x = scan.get_stdfloat();
1435 _y = scan.get_stdfloat();
1436 _z = scan.get_stdfloat();
1437 for(int i=0; i < 6; i++) {
1438 _planes[i].read_datagram(scan);
1439 }
1440 for(int i=0; i < 6; i++) {
1441 _to_2d_mat[i].read_datagram(scan);
1442 }
1443 for(int i=0; i < 6; i++) {
1444 size_t size = scan.get_uint16();
1445 for (size_t j = 0; j < size; j++) {
1446 LPoint2 p;
1447 LVector2 v;
1448 p.read_datagram(scan);
1449 v.read_datagram(scan);
1450 _points[i].push_back(PointDef(p, v));
1451 }
1452 }
1453}
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...
This functions similarly to a LightAttrib.
get_on_plane
Returns the nth plane enabled by the attribute, sorted in render order.
get_num_on_planes
Returns the number of planes that are enabled by the attribute.
A cuboid collision volume or object.
static void register_with_read_factory()
Factory method to generate a CollisionBox object.
PN_stdfloat dist_to_polygon(const LPoint2 &p, const Points &points) const
Returns the linear distance from the 2-d point to the nearest part of the polygon defined by the poin...
void setup_points(const LPoint3 *begin, const LPoint3 *end, int plane)
Computes the plane and 2d projection of points that make up this side.
bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa, const TransformState *net_transform, int plane_no) const
Clips the polygon by all of the clip planes named in the clip plane attribute and fills new_points up...
bool clip_polygon(Points &new_points, const Points &source_points, const LPlane &plane, int plane_no) const
Clips the source_points of the polygon by the indicated clipping plane, and modifies new_points to re...
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const
Fills the indicated matrix with the appropriate rotation transform to move points from the 2-d plane ...
static void compute_vectors(Points &points)
Now that the _p members of the given points array have been computed, go back and compute all of the ...
void setup_box()
Compute parameters for each of the box's sides.
LPlane get_plane(int n) const
Returns the nth face of the rectangular solid.
bool point_is_inside(const LPoint2 &p, const Points &points) const
Returns true if the indicated point is within the polygon's 2-d space, false otherwise.
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
LPoint2 to_2d(const LVecBase3 &point3d, int plane) const
Assuming the indicated point in 3-d space lies within the polygon's plane, returns the corresponding ...
LPoint3 get_point(int n) const
Returns the nth vertex of the OBB.
virtual LPoint3 get_collision_origin() const
Returns the point in space deemed to be the "origin" of the solid for collision purposes.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
LPlane set_plane(int n) const
Creates the nth face of the rectangular solid.
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.
An infinite ray, with a specific origin and direction.
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.
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...
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition datagram.I:85
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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 disconnected line segments.
Definition geomLines.h:23
Defines a series of disconnected triangles.
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
PandaNode * node() const
Returns the referenced node of the path.
Definition nodePath.I:227
A lightweight class that represents a single element that may be timed and/or counted via stats.
A node that contains a plane.
Definition planeNode.h:36
get_clip_effect
Returns the clip_effect bits for this clip plane.
Definition planeNode.h:71
get_plane
Returns the plane represented by the PlaneNode.
Definition planeNode.h:68
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.
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.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.