mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
parent
b558bc8bfb
commit
674c9fdee9
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "collisionCapsule.h"
|
#include "collisionCapsule.h"
|
||||||
|
#include "collisionBox.h"
|
||||||
#include "collisionSphere.h"
|
#include "collisionSphere.h"
|
||||||
#include "collisionLine.h"
|
#include "collisionLine.h"
|
||||||
#include "collisionRay.h"
|
#include "collisionRay.h"
|
||||||
@ -140,6 +141,87 @@ compute_internal_bounds() const {
|
|||||||
return bound;
|
return bound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PT(CollisionEntry) CollisionCapsule::
|
||||||
|
test_intersection_from_box(const CollisionEntry &entry) const {
|
||||||
|
const CollisionBox *box;
|
||||||
|
DCAST_INTO_R(box, entry.get_from(), nullptr);
|
||||||
|
|
||||||
|
const LMatrix4 wrt_mat = entry.get_wrt_mat();
|
||||||
|
const LMatrix4 inv_wrt_mat = entry.get_inv_wrt_mat();
|
||||||
|
|
||||||
|
// We find the closest point on the box to the line
|
||||||
|
// segment defined by the endpoints of the capsule. To do
|
||||||
|
// this, convert the capsule into the box's coordinate space.
|
||||||
|
LPoint3 box_min = box->get_min();
|
||||||
|
LPoint3 box_max = box->get_max();
|
||||||
|
LPoint3 a = _a * inv_wrt_mat;
|
||||||
|
LPoint3 b = _b * inv_wrt_mat;
|
||||||
|
LVector3 delta = b - a;
|
||||||
|
|
||||||
|
double min_dist = DBL_MAX;
|
||||||
|
PN_stdfloat t;
|
||||||
|
LPoint3 closest_point;
|
||||||
|
// Iterate through the 6 planes of the box
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (!box->get_plane(i).intersects_line(t, a, delta)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (t > 1.0) t = 1.0;
|
||||||
|
if (t < 0.0) t = 0.0;
|
||||||
|
if (t == min_dist) continue;
|
||||||
|
LPoint3 intersection_point = a + delta * t;
|
||||||
|
// Find the closest point on the box to the intersection point
|
||||||
|
LPoint3 p = intersection_point.fmax(box_min).fmin(box_max);
|
||||||
|
double dist = (p - intersection_point).length_squared();
|
||||||
|
if (dist < min_dist) {
|
||||||
|
min_dist = dist;
|
||||||
|
closest_point = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Convert the closest point to the capsule's coordinate
|
||||||
|
// space where the capsule is aligned with the y axis.
|
||||||
|
closest_point = closest_point * wrt_mat * _inv_mat;
|
||||||
|
a = _a * _inv_mat;
|
||||||
|
b = _b * _inv_mat;
|
||||||
|
|
||||||
|
// Find the closest point on the line segment
|
||||||
|
LPoint3 point_on_segment;
|
||||||
|
point_on_segment[0] = a[0];
|
||||||
|
point_on_segment[2] = a[2];
|
||||||
|
if (closest_point[1] < std::min(a[1], b[1])) {
|
||||||
|
point_on_segment[1] = std::min(a[1], b[1]);
|
||||||
|
} else if (closest_point[1] > std::max(a[1], b[1])) {
|
||||||
|
point_on_segment[1] = std::max(a[1], b[1]);
|
||||||
|
} else {
|
||||||
|
point_on_segment[1] = closest_point[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((closest_point - point_on_segment).length_squared() > _radius * _radius) {
|
||||||
|
// No intersection.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PT(CollisionEntry) new_entry = new CollisionEntry(entry);
|
||||||
|
LVector3 normal;
|
||||||
|
if (point_on_segment == closest_point) {
|
||||||
|
// If the box directly intersects the line segment, use
|
||||||
|
// the box's center to determine the surface normal.
|
||||||
|
normal = (box->get_center() * wrt_mat * _inv_mat) - point_on_segment;
|
||||||
|
if (closest_point != a && closest_point != b) normal[1] = 0;
|
||||||
|
} else {
|
||||||
|
normal = (closest_point - point_on_segment);
|
||||||
|
}
|
||||||
|
normal.normalize();
|
||||||
|
LPoint3 surface_point = point_on_segment + normal * _radius;
|
||||||
|
new_entry->set_interior_point(_mat.xform_point(closest_point));
|
||||||
|
new_entry->set_surface_point(_mat.xform_point(surface_point));
|
||||||
|
new_entry->set_surface_normal(_mat.xform_vec(normal));
|
||||||
|
return new_entry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -74,6 +74,8 @@ protected:
|
|||||||
virtual PT(BoundingVolume) compute_internal_bounds() const;
|
virtual PT(BoundingVolume) compute_internal_bounds() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual PT(CollisionEntry)
|
||||||
|
test_intersection_from_box(const CollisionEntry &entry) const;
|
||||||
virtual PT(CollisionEntry)
|
virtual PT(CollisionEntry)
|
||||||
test_intersection_from_sphere(const CollisionEntry &entry) const;
|
test_intersection_from_sphere(const CollisionEntry &entry) const;
|
||||||
virtual PT(CollisionEntry)
|
virtual PT(CollisionEntry)
|
||||||
|
39
tests/collide/test_into_capsule.py
Normal file
39
tests/collide/test_into_capsule.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from collisions import *
|
||||||
|
|
||||||
|
def test_box_into_capsule():
|
||||||
|
capsule = CollisionCapsule((1, 0, 0), (-1, 0, 0), .5)
|
||||||
|
|
||||||
|
# colliding just on the capsule's cylinder
|
||||||
|
box = CollisionBox((0, 1, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is not None
|
||||||
|
|
||||||
|
# no longer colliding
|
||||||
|
box = CollisionBox((0, 1.1, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is None
|
||||||
|
|
||||||
|
# box is inside the capsule's cylinder
|
||||||
|
box = CollisionBox((0, .8, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is not None
|
||||||
|
|
||||||
|
# colliding with the first endcap
|
||||||
|
box = CollisionBox((2, 0, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is not None
|
||||||
|
|
||||||
|
# almost colliding with first endcap
|
||||||
|
box = CollisionBox((2.01, 0, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is None
|
||||||
|
|
||||||
|
# colliding with the second endcap
|
||||||
|
box = CollisionBox((-2, 0, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is not None
|
||||||
|
|
||||||
|
# almost colliding with second endcap
|
||||||
|
box = CollisionBox((-2.01, 0, 0), .5, .5, .5)
|
||||||
|
entry = make_collision(box, capsule)[0]
|
||||||
|
assert entry is None
|
Loading…
x
Reference in New Issue
Block a user