瀏覽代碼

collide: add collision test for parabola into box

Closes #664
hecris 6 年之前
父節點
當前提交
47f39809ee
共有 3 個文件被更改,包括 150 次插入0 次删除
  1. 77 0
      panda/src/collide/collisionBox.cxx
  2. 2 0
      panda/src/collide/collisionBox.h
  3. 71 0
      tests/collide/test_into_box.py

+ 77 - 0
panda/src/collide/collisionBox.cxx

@@ -16,6 +16,7 @@
 #include "collisionRay.h"
 #include "collisionSphere.h"
 #include "collisionSegment.h"
+#include "collisionParabola.h"
 #include "collisionCapsule.h"
 #include "collisionHandler.h"
 #include "collisionEntry.h"
@@ -493,6 +494,82 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
   return new_entry;
 }
 
+PT(CollisionEntry) CollisionBox::
+test_intersection_from_parabola(const CollisionEntry &entry) const {
+  const CollisionParabola *parabola;
+  DCAST_INTO_R(parabola, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  // Convert the parabola into local coordinate space.
+  LParabola local_p(parabola->get_parabola());
+  local_p.xform(wrt_mat);
+
+  PN_stdfloat t = INT_MAX;
+  PN_stdfloat t1, t2;
+  int intersecting_face = -1;
+  for (int i = 0; i < get_num_planes(); i++) {
+    LPlane face = get_plane(i);
+    if (!face.intersects_parabola(t1, t2, local_p)) {
+      // the parabola does not intersect this face, skip to the next one
+      continue;
+    }
+    PN_stdfloat ts[2] = {t1, t2};
+    // iterate through the t values to see if each of them are within our
+    // parabola and the intersection point is behind all other faces
+    for (int j = 0; j < 2; j++) {
+      PN_stdfloat cur_t = ts[j];
+      if (cur_t > t) {
+        // we are looking for the earliest t value
+        // if this t value is greater, don't bother checking it
+        continue;
+      }
+      if (cur_t >= parabola->get_t1() && cur_t <= parabola->get_t2()) {
+        // the parabola does intersect this plane, now we check
+        // if the intersection point is behind all other planes
+        bool behind = true;
+        for (int k = 0; k < get_num_planes(); k++) {
+          if (k == i) {
+            // no need to check the intersecting face
+            continue;
+          }
+          if (get_plane(k).dist_to_plane(local_p.calc_point(cur_t)) > 0.0f) {
+            // our point is in front of this face, turns out the parabola
+            // does not collide with the box at this point
+            behind = false;
+            break;
+          }
+        }
+        if (behind) {
+          // the parabola does indeed collide with the box at this point
+          t = cur_t;
+          intersecting_face = i;
+        }
+      }
+    }
+  }
+
+  if (intersecting_face != -1) {
+    if (collide_cat.is_debug()) {
+      collide_cat.debug()
+        << "intersection detected from " << entry.get_from_node_path()
+        << " into " << entry.get_into_node_path() << "\n";
+    }
+    PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+
+    LPlane face = get_plane(intersecting_face);
+
+    LPoint3 into_intersection_point = local_p.calc_point(t);
+    LVector3 normal = (has_effective_normal() && parabola->get_respect_effective_normal()) ? get_effective_normal() : face.get_normal();
+
+    new_entry->set_surface_point(into_intersection_point);
+    new_entry->set_surface_normal(normal);
+    return new_entry;
+  } else {
+    return nullptr;
+  }
+}
+
 /**
  * Double dispatch point for segment as a FROM object
  */

+ 2 - 0
panda/src/collide/collisionBox.h

@@ -81,6 +81,8 @@ protected:
     test_intersection_from_ray(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
     test_intersection_from_segment(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+    test_intersection_from_parabola(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
     test_intersection_from_capsule(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)

+ 71 - 0
tests/collide/test_into_box.py

@@ -43,3 +43,74 @@ def test_ray_into_box():
     # No collision
     entry = make_collision(CollisionRay(0, 0, 100, 1, 0, 0), box)[0]
     assert entry is None
+
+
+def test_parabola_into_box():
+    # Set up Parabola and Box
+    parabola = CollisionParabola()
+    parabola.set_t1(0)
+    parabola.set_t2(2)
+    box = CollisionBox((0,0,0), 3, 3, 3)
+
+    # Parabola is inside the Box and not colliding
+    parabola.set_parabola(
+            LParabola((-1, 0, -1), (1, 0, 1), (1, 1, 1)))
+    entry = make_collision(parabola, box)[0]
+    assert entry is None
+
+    # Parabola is inside the Box and colliding on its projectile
+    parabola.set_parabola(
+            LParabola((0, 0, 1), (0, 0, 1), (1, 1, 1)))
+    # Parabola collides with Box when t == 1 at point (1, 1, 3)
+    assert parabola.get_parabola().calc_point(1)  == (1, 1, 3)
+    entry, np_from, into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == (1, 1, 3)
+    assert entry.get_from() == parabola
+    assert entry.get_into() == box
+
+    # Parabola is inside the Box and colliding on one of the endpoints
+    parabola.set_parabola(
+            LParabola((0, 0, 0), (0, 0, 1), (-3, 0, -3)))
+    entry, np_from, np_into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == (-3, 0, -3)
+
+    # Parabola is outside the Box and not colliding
+    parabola.set_parabola(
+            LParabola((0, 0, 0), (0, 0, 1), (-5, 0, 0)))
+    entry = make_collision(parabola, box)[0]
+    assert entry is None
+
+    # Parabola is outside the Box and colliding on its projecticle
+    parabola.set_parabola(
+            LParabola((-2, -2, -2), (1, 1, 1), (4, 4, 4)))
+    # Parabola collides with Box when t == 1 at point (3, 3, 3)
+    assert parabola.get_parabola().calc_point(1) == (3, 3, 3)
+    entry, np_from, into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == (3, 3, 3)
+
+    # Parabola is outside the Box and colliding on the first endpoint
+    parabola.set_parabola(
+            LParabola((1, 1, 1), (1, 1, 1), (3, 3, 3)))
+    entry, np_from, np_into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == (3, 3, 3)
+
+    # Parabola is outside the Box and colliding on the second endpoint
+    parabola.set_parabola(
+            LParabola((1, 0, 1), (-1, 0, -1), (-5, -3, -5)))
+    assert parabola.get_parabola().calc_point(2) == (-3, -3, -3)
+    entry, np_from, np_into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == (-3, -3, -3)
+
+    # Parabola intersects the Box at two points,
+    # t == 0 and t == 2 the earliest one should be chosen.
+    parabola.set_parabola(
+            LParabola((-1, -1, -1), (-1, -1, -1), (3, 3, 3)))
+    entry, np_from, np_into = make_collision(parabola, box)
+    assert entry.get_surface_point(np_from) == parabola.get_parabola().calc_point(0)
+
+    # First point no longer intersecting
+    parabola.set_t1(1)
+    entry, np_from, np_into = make_collision(parabola, box)
+    assert parabola.get_parabola().calc_point(2) == (-3, -3, -3)
+    assert entry.get_surface_point(np_from) == parabola.get_parabola().calc_point(2)
+    assert entry.get_surface_normal(np_from) is not None