Initial commit.
This commit is contained in:
commit
44b5f2392f
98 changed files with 11750 additions and 0 deletions
514
physics/shape.cpp
Normal file
514
physics/shape.cpp
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Projection::Projection() : length(0) {
|
||||
}
|
||||
|
||||
Projection::Projection(const Vector& x, float length)
|
||||
: x(x), length(length) {
|
||||
}
|
||||
|
||||
Projection::Projection(const Vector& x, float length, const Vector& normal)
|
||||
: x(x), length(length), normal(normal) {
|
||||
}
|
||||
|
||||
bool Projection::operator<(const Projection& p) const {
|
||||
return fabsf(length) < fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator<=(const Projection& p) const {
|
||||
return fabsf(length) <= fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator>(const Projection& p) const {
|
||||
return fabsf(length) > fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator>=(const Projection& p) const {
|
||||
return fabsf(length) >= fabsf(p.length);
|
||||
}
|
||||
|
||||
Sphere::Sphere()
|
||||
: r_(0.f), r2_(0.f) {
|
||||
}
|
||||
|
||||
Sphere::Sphere(const Vector& x, float radius)
|
||||
: x_(x), r_(radius), r2_(radius * radius) {
|
||||
}
|
||||
|
||||
bool Sphere::above(const Plane& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return p.n_.dot(x_ - p.x_) > r_;
|
||||
}
|
||||
|
||||
bool Sphere::below(const Plane& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return p.n_.dot(x_ - p.x_) < -r_;
|
||||
}
|
||||
|
||||
bool Sphere::intersects(const Sphere& s) const {
|
||||
/* Derived from Moller-Haines section 16.13.1. */
|
||||
float r = r_ + s.r_;
|
||||
return (s.x_ - x_).squared() < (r * r);
|
||||
}
|
||||
|
||||
Projection Sphere::project(const Vector& x) const {
|
||||
Vector v = x - x_;
|
||||
float l = v.length();
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
return Projection(x - n * d, fabsf(d), (d < 0.f) ? -n : n);
|
||||
}
|
||||
|
||||
LineSegment::LineSegment() {
|
||||
}
|
||||
|
||||
LineSegment::LineSegment(const Vector& x0, const Vector& x1)
|
||||
: x0_(x0), x1_(x1), x01_(x1 - x0), l2_(x01_.dot(x01_)) {
|
||||
}
|
||||
|
||||
bool LineSegment::intersects(const Sphere& s) const {
|
||||
/* Derived from Point-Line Distance--3-Dimensional on MathWorld. */
|
||||
return (x01_.cross(x0_ - s.x_).squared() / l2_) < s.r2_;
|
||||
}
|
||||
|
||||
Projection LineSegment::project(const Vector& p) const {
|
||||
/* Derived from Point-Line Distance--3-Dimensional on MathWorld. */
|
||||
float t = (p - x0_).dot(x01_) / l2_;
|
||||
Vector x = (t <= 0.f) ? x0_ : ((t >= 1.f) ? x1_ : (x0_ + x01_ * t));
|
||||
return Projection(x, (p - x).length());
|
||||
}
|
||||
|
||||
Plane::Plane() {
|
||||
}
|
||||
|
||||
Plane::Plane(const Vector& x, const Vector& normal)
|
||||
: x_(x), n_(normal) {
|
||||
}
|
||||
|
||||
bool Plane::intersects(const Sphere& s) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return fabsf(n_.dot(s.x_ - x_)) < s.r_;
|
||||
}
|
||||
|
||||
Projection Plane::project(const Vector& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
float l = n_.dot(p - x_);
|
||||
return Projection(p - n_ * l, fabsf(l), (l < 0.f) ? -n_ : n_);
|
||||
}
|
||||
|
||||
Triangle::Triangle() {
|
||||
}
|
||||
|
||||
Triangle::Triangle(const Vector& x0, const Vector& x1, const Vector& x2)
|
||||
: x01_(x0, x1), x12_(x1, x2), x20_(x2, x0),
|
||||
p_(x0, (x1 - x0).cross(x2 - x1).normalize()) {
|
||||
}
|
||||
|
||||
bool Triangle::intersects(const Sphere& s) const {
|
||||
/* Derived from ERIT section 2.3 (reordered). */
|
||||
Projection p0 = p_.project(s.x_);
|
||||
if (p0.length > s.r_) {
|
||||
return false;
|
||||
}
|
||||
if (x01_.intersects(s) || x12_.intersects(s) || x20_.intersects(s)) {
|
||||
return true;
|
||||
}
|
||||
return contains(p0.x);
|
||||
}
|
||||
|
||||
Projection Triangle::project(const Vector& p) const {
|
||||
Projection pp = p_.project(p);
|
||||
if (contains(pp.x)) {
|
||||
return pp;
|
||||
}
|
||||
Projection p01 = x01_.project(pp.x);
|
||||
Projection p12 = x12_.project(pp.x);
|
||||
Projection p20 = x20_.project(pp.x);
|
||||
Projection pb = std::min(std::min(p01, p12), p20);
|
||||
pb.length = sqrtf(pb.length * pb.length + pp.length * pp.length);
|
||||
pb.normal = pp.normal;
|
||||
return pb;
|
||||
}
|
||||
|
||||
bool Triangle::contains(const Vector& p) const {
|
||||
return ((x0() - x1()).cross(p - x0()).dot(p_.normal()) < 0.f)
|
||||
&& ((x1() - x2()).cross(p - x1()).dot(p_.normal()) < 0.f)
|
||||
&& ((x2() - x0()).cross(p - x2()).dot(p_.normal()) < 0.f);
|
||||
}
|
||||
|
||||
Quad::Quad() {
|
||||
}
|
||||
|
||||
Quad::Quad(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: x01_(x0, x1), x12_(x1, x2), x23_(x2, x3), x30_(x3, x0),
|
||||
p_(x0, (x1 - x0).cross(x2 - x1).normalize()) {
|
||||
}
|
||||
|
||||
bool Quad::intersects(const Sphere& s) const {
|
||||
/* Derived from Triangle-Sphere test above. */
|
||||
Projection p0 = p_.project(s.x());
|
||||
if (p0.length > s.radius()) {
|
||||
return false;
|
||||
}
|
||||
if (x01_.intersects(s)
|
||||
|| x12_.intersects(s)
|
||||
|| x23_.intersects(s)
|
||||
|| x30_.intersects(s)) {
|
||||
return true;
|
||||
}
|
||||
return contains(p0.x);
|
||||
}
|
||||
|
||||
Projection Quad::project(const Vector& p) const {
|
||||
Projection pp = p_.project(p);
|
||||
if (contains(pp.x)) {
|
||||
return pp;
|
||||
}
|
||||
Projection p01 = x01_.project(pp.x);
|
||||
Projection p12 = x12_.project(pp.x);
|
||||
Projection p23 = x23_.project(pp.x);
|
||||
Projection p30 = x30_.project(pp.x);
|
||||
Projection pb = std::min(std::min(std::min(p01, p12), p23), p30);
|
||||
pb.length = sqrtf(pb.length * pb.length + pp.length * pp.length);
|
||||
pb.normal = pp.normal;
|
||||
return pb;
|
||||
}
|
||||
|
||||
bool Quad::contains(const Vector& p) const {
|
||||
return ((x0() - x1()).cross(p - x0()).dot(p_.normal()) < 0.f)
|
||||
&& ((x1() - x2()).cross(p - x1()).dot(p_.normal()) < 0.f)
|
||||
&& ((x2() - x3()).cross(p - x2()).dot(p_.normal()) < 0.f)
|
||||
&& ((x3() - x0()).cross(p - x3()).dot(p_.normal()) < 0.f);
|
||||
}
|
||||
|
||||
Wedge::Wedge() {
|
||||
}
|
||||
|
||||
Wedge::Wedge(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: top_(x0, x1, x2, x3),
|
||||
right_(x2, x1, Vector(x1.x, x0.y, x1.z), Vector(x2.x, x0.y, x2.z)),
|
||||
bottom_(x0, x3, right_.x3(), right_.x2()),
|
||||
front_(x1, x0, right_.x2()),
|
||||
back_(x3, x2, right_.x3()) {
|
||||
}
|
||||
|
||||
bool Wedge::intersects(const Sphere& s) const {
|
||||
/* Derived (very approximately) from Moller-Haines section 16.14.2. */
|
||||
if (s.above(top_.p_)
|
||||
|| s.above(right_.p_)
|
||||
|| s.above(front_.p_)
|
||||
|| s.above(back_.p_)
|
||||
|| s.above(bottom_.p_)) {
|
||||
return false;
|
||||
}
|
||||
if (s.below(top_.p_)
|
||||
&& s.below(right_.p_)
|
||||
&& s.below(front_.p_)
|
||||
&& s.below(back_.p_)
|
||||
&& s.below(bottom_.p_)) {
|
||||
return true;
|
||||
}
|
||||
return top_.intersects(s)
|
||||
|| right_.intersects(s)
|
||||
|| front_.intersects(s)
|
||||
|| back_.intersects(s)
|
||||
|| bottom_.intersects(s);
|
||||
}
|
||||
|
||||
Projection Wedge::project(const Vector& p) const {
|
||||
Projection pt = top_.project(p);
|
||||
Projection pr = right_.project(p);
|
||||
Projection pf = front_.project(p);
|
||||
Projection pb = back_.project(p);
|
||||
Projection pd = bottom_.project(p);
|
||||
return std::min(std::min(std::min(std::min(pt, pr), pf), pb), pd);
|
||||
}
|
||||
|
||||
AxisAlignedBox::AxisAlignedBox() {
|
||||
}
|
||||
|
||||
AxisAlignedBox::AxisAlignedBox(const Vector& min, const Vector& max)
|
||||
: min_(min), max_(max) {
|
||||
}
|
||||
|
||||
bool AxisAlignedBox::intersects(const Sphere& s) const {
|
||||
/* Derived from Moller-Haines section 16.13.2. */
|
||||
Vector e = Vector::max(min_ - s.x_, Vector::ZERO())
|
||||
+ Vector::max(s.x_ - max_, Vector::ZERO());
|
||||
return e.squared() < s.r2_;
|
||||
}
|
||||
|
||||
bool AxisAlignedBox::contains(const Vector& p) const {
|
||||
return ((p.x >= min_.x) && (p.x < max_.x)
|
||||
&& (p.y >= min_.y) && (p.y < max_.y)
|
||||
&& (p.z >= min_.z) && (p.z < max_.z));
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::project(const Vector& p) const {
|
||||
return contains(p) ? projectOut(p) : projectIn(p);
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::projectOut(const Vector& p) const {
|
||||
Vector x = p;
|
||||
Vector n;
|
||||
float l;
|
||||
|
||||
float dx1 = p.x - min_.x;
|
||||
float dx2 = max_.x - p.x;
|
||||
float dx = std::min(dx1, dx2);
|
||||
|
||||
float dy1 = p.y - min_.y;
|
||||
float dy2 = max_.y - p.y;
|
||||
float dy = std::min(dy1, dy2);
|
||||
|
||||
float dz1 = p.z - min_.z;
|
||||
float dz2 = max_.z - p.z;
|
||||
float dz = std::min(dz1, dz2);
|
||||
|
||||
if ((dx <= dy) && (dx <= dz)) {
|
||||
if (dx1 <= dx2) {
|
||||
l = dx1;
|
||||
x.x = min_.x;
|
||||
n.x = 1.f;
|
||||
} else {
|
||||
l = dx2;
|
||||
x.x = max_.x;
|
||||
n.x = -1.f;
|
||||
}
|
||||
} else if (dy <= dz) {
|
||||
if (dy1 <= dy2) {
|
||||
l = dy1;
|
||||
x.y = min_.y;
|
||||
n.y = 1.f;
|
||||
} else {
|
||||
l = dy2;
|
||||
x.y = max_.y;
|
||||
n.y = -1.f;
|
||||
}
|
||||
} else {
|
||||
if (dz1 <= dz2) {
|
||||
l = dz1;
|
||||
x.z = min_.z;
|
||||
n.z = 1.f;
|
||||
} else {
|
||||
l = dz2;
|
||||
x.z = max_.z;
|
||||
n.z = -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
return Projection(x, -l, n);
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::projectIn(const Vector& p) const {
|
||||
Vector x = p;
|
||||
Vector n;
|
||||
|
||||
if (p.x < min_.x) {
|
||||
x.x = min_.x;
|
||||
n = -Vector::X();
|
||||
} else if (p.x >= max_.x) {
|
||||
x.x = max_.x;
|
||||
n = Vector::X();
|
||||
}
|
||||
|
||||
if (p.z < min_.z) {
|
||||
x.z = min_.z;
|
||||
n = -Vector::Z();
|
||||
} else if (p.z >= max_.z) {
|
||||
x.z = max_.z;
|
||||
n = Vector::Z();
|
||||
}
|
||||
|
||||
if (p.y < min_.y) {
|
||||
x.y = min_.y;
|
||||
n = -Vector::Y();
|
||||
} else if (p.y >= max_.y) {
|
||||
x.y = max_.y;
|
||||
n = Vector::Y();
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: If the particle does not project directly onto the triangle (and
|
||||
* similarly for quads), we should interpolate the normal of the projection so
|
||||
* that particles appear to bounce off the corner.
|
||||
*
|
||||
* On the other hand, this leads to some weird behavior when applying
|
||||
* directional friction when Polly is on the edge of a platform. So for now,
|
||||
* just use axis normals.
|
||||
*/
|
||||
|
||||
return Projection(x, (p - x).length(), n);
|
||||
}
|
||||
|
||||
Box::Box() {
|
||||
}
|
||||
|
||||
Box::Box(const AxisAlignedBox& box)
|
||||
: top_(box.x4(), box.x5(), box.x6(), box.x7()),
|
||||
bottom_(box.x0(), box.x1(), box.x2(), box.x3()),
|
||||
left_(box.x0(), box.x3(), box.x6(), box.x5()),
|
||||
right_(box.x1(), box.x4(), box.x7(), box.x2()),
|
||||
front_(box.x2(), box.x7(), box.x6(), box.x3()),
|
||||
back_(box.x0(), box.x5(), box.x4(), box.x1()) {
|
||||
}
|
||||
|
||||
Box::Box(const Vector& c, const Vector& x, const Vector& y, const Vector& z) {
|
||||
Vector x0 = c - x - y - z;
|
||||
Vector x1 = c + x - y - z;
|
||||
Vector x2 = c + x - y + z;
|
||||
Vector x3 = c - x - y + z;
|
||||
Vector x4 = c + x + y - z;
|
||||
Vector x5 = c - x + y - z;
|
||||
Vector x6 = c - x + y + z;
|
||||
Vector x7 = c + x + y + z;
|
||||
top_ = Quad(x4, x5, x6, x7);
|
||||
bottom_ = Quad(x0, x1, x2, x3);
|
||||
left_ = Quad(x0, x3, x6, x5);
|
||||
right_ = Quad(x1, x4, x7, x2);
|
||||
front_ = Quad(x2, x7, x6, x3);
|
||||
back_ = Quad(x0, x5, x4, x1);
|
||||
}
|
||||
|
||||
Box::Box(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3,
|
||||
const Vector& x4, const Vector& x5, const Vector& x6, const Vector& x7)
|
||||
: top_(x4, x5, x6, x7),
|
||||
bottom_(x0, x1, x2, x3),
|
||||
left_(x0, x3, x6, x5),
|
||||
right_(x1, x4, x7, x2),
|
||||
front_(x2, x7, x6, x3),
|
||||
back_(x0, x5, x4, x1) {
|
||||
}
|
||||
|
||||
bool Box::intersects(const Sphere& s) const {
|
||||
/* Derived (very approximately) from Moller-Haines section 16.14.2. */
|
||||
if (s.above(top_.plane())
|
||||
|| s.above(left_.plane())
|
||||
|| s.above(right_.plane())
|
||||
|| s.above(front_.plane())
|
||||
|| s.above(back_.plane())
|
||||
|| s.above(bottom_.plane())) {
|
||||
return false;
|
||||
}
|
||||
if (s.below(top_.plane())
|
||||
&& s.below(left_.plane())
|
||||
&& s.below(right_.plane())
|
||||
&& s.below(front_.plane())
|
||||
&& s.below(back_.plane())
|
||||
&& s.below(bottom_.plane())) {
|
||||
return true;
|
||||
}
|
||||
return top_.intersects(s)
|
||||
|| left_.intersects(s)
|
||||
|| right_.intersects(s)
|
||||
|| front_.intersects(s)
|
||||
|| back_.intersects(s)
|
||||
|| bottom_.intersects(s);
|
||||
}
|
||||
|
||||
Projection Box::project(const Vector& v) const {
|
||||
Projection p = top_.project(v);
|
||||
p = std::min(p, bottom_.project(v));
|
||||
p = std::min(p, left_.project(v));
|
||||
p = std::min(p, right_.project(v));
|
||||
p = std::min(p, front_.project(v));
|
||||
p = std::min(p, back_.project(v));
|
||||
return p;
|
||||
}
|
||||
|
||||
Cylinder::Cylinder() {
|
||||
}
|
||||
|
||||
Cylinder::Cylinder(const Vector& x0, const Vector& x1, float radius)
|
||||
: axis_(x0, x1), l_(sqrtf(axis_.l2_)), r_(radius), v_(axis_.x01_ / l_) {
|
||||
}
|
||||
|
||||
bool Cylinder::intersects(const Sphere& s) const {
|
||||
/* Derived from ERIT section 2.5. */
|
||||
Vector pa = s.x_ - axis_.x0_;
|
||||
float pqdotpa = axis_.x01_.dot(pa);
|
||||
float rs1;
|
||||
|
||||
/* P -> Q -> B */
|
||||
if (pqdotpa > axis_.l2_) {
|
||||
float f = pqdotpa - axis_.l2_;
|
||||
float d2 = (f * f) / axis_.l2_;
|
||||
if (d2 > s.r2_) {
|
||||
return false;
|
||||
}
|
||||
rs1 = sqrtf(s.r2_ - d2);
|
||||
}
|
||||
|
||||
/* B -> P -> Q */
|
||||
else if (pqdotpa < 0.f) {
|
||||
float qpdotqa = -axis_.x01_.dot(s.x_ - axis_.x1_);
|
||||
float f = qpdotqa - axis_.l2_;
|
||||
float d2 = (f * f) / axis_.l2_;
|
||||
if (d2 > s.r2_) {
|
||||
return false;
|
||||
}
|
||||
rs1 = sqrtf(s.r2_ - d2);
|
||||
}
|
||||
|
||||
/* P -> B -> Q */
|
||||
else {
|
||||
rs1 = s.r_;
|
||||
}
|
||||
|
||||
float ld2 = axis_.x01_.cross(pa).squared() / axis_.l2_;
|
||||
float r = r_ + rs1;
|
||||
return ld2 <= (r * r);
|
||||
}
|
||||
|
||||
Projection Cylinder::project(const Vector& p) const {
|
||||
/* Derived from ERIT section 2.5. */
|
||||
Vector pa = p - axis_.x0_;
|
||||
float pqdotpa = axis_.x01_.dot(pa);
|
||||
|
||||
/* P -> Q -> B */
|
||||
if (pqdotpa > axis_.l2_) {
|
||||
Vector x = Plane(axis_.x1_, v_).project(p).x;
|
||||
|
||||
Vector v = x - axis_.x1_;
|
||||
float l = v.length();
|
||||
if (l > r_) {
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
x -= n * d;
|
||||
}
|
||||
|
||||
return Projection(x, (p - x).length(), v_);
|
||||
}
|
||||
|
||||
/* B -> P -> Q */
|
||||
else if (pqdotpa < 0.f) {
|
||||
Vector x = Plane(axis_.x0_, v_).project(p).x;
|
||||
|
||||
Vector v = x - axis_.x0_;
|
||||
float l = v.length();
|
||||
if (l > r_) {
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
x -= n * d;
|
||||
}
|
||||
|
||||
return Projection(x, (p - x).length(), -v_);
|
||||
}
|
||||
|
||||
/* P -> B -> Q */
|
||||
Vector b = axis_.x0_ + axis_.x01_ * pqdotpa / axis_.l2_;
|
||||
Vector v = p - b;
|
||||
float l = v.length();
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
return Projection(p - n * d, fabsf(d), (d < 0.f) ? -n : n);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue