mirror of
https://github.com/photonstorm/phaser
synced 2024-12-04 18:40:59 +00:00
559 lines
No EOL
18 KiB
TypeScript
559 lines
No EOL
18 KiB
TypeScript
/// <reference path="../math/Vec2.ts" />
|
|
/// <reference path="../geom/Point.ts" />
|
|
/// <reference path="../math/Vec2Utils.ts" />
|
|
/// <reference path="shapes/Shape.ts" />
|
|
/// <reference path="shapes/Circle.ts" />
|
|
/// <reference path="shapes/Poly.ts" />
|
|
/// <reference path="shapes/Segment.ts" />
|
|
/// <reference path="AdvancedPhysics.ts" />
|
|
/// <reference path="Body.ts" />
|
|
/// <reference path="Contact.ts" />
|
|
|
|
/**
|
|
* Phaser - Advanced Physics - Collision Handlers
|
|
*
|
|
* Based on the work Ju Hyung Lee started in JS PhyRus.
|
|
*/
|
|
|
|
module Phaser.Physics {
|
|
|
|
export class Collision {
|
|
|
|
public collide(a, b, contacts: Contact[]) {
|
|
|
|
// Circle (a is the circle)
|
|
if (a.type == AdvancedPhysics.SHAPE_TYPE_CIRCLE)
|
|
{
|
|
if (b.type == AdvancedPhysics.SHAPE_TYPE_CIRCLE)
|
|
{
|
|
return this.circle2Circle(a, b, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_SEGMENT)
|
|
{
|
|
return this.circle2Segment(a, b, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_POLY)
|
|
{
|
|
return this.circle2Poly(a, b, contacts);
|
|
}
|
|
}
|
|
|
|
// Segment (a is the segment)
|
|
if (a.type == AdvancedPhysics.SHAPE_TYPE_SEGMENT)
|
|
{
|
|
if (b.type == AdvancedPhysics.SHAPE_TYPE_CIRCLE)
|
|
{
|
|
return this.circle2Segment(b, a, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_SEGMENT)
|
|
{
|
|
return this.segment2Segment(a, b, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_POLY)
|
|
{
|
|
return this.segment2Poly(a, b, contacts);
|
|
}
|
|
}
|
|
|
|
// Poly (a is the poly)
|
|
if (a.type == AdvancedPhysics.SHAPE_TYPE_POLY)
|
|
{
|
|
if (b.type == AdvancedPhysics.SHAPE_TYPE_CIRCLE)
|
|
{
|
|
return this.circle2Poly(b, a, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_SEGMENT)
|
|
{
|
|
return this.segment2Poly(b, a, contacts);
|
|
}
|
|
else if (b.type == AdvancedPhysics.SHAPE_TYPE_POLY)
|
|
{
|
|
return this.poly2Poly(a, b, contacts);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private _circle2Circle(c1, r1, c2, r2, contactArr) {
|
|
|
|
var rmax = r1 + r2;
|
|
|
|
var t: Phaser.Vec2 = new Phaser.Vec2;
|
|
//var t = vec2.sub(c2, c1);
|
|
Phaser.Vec2Utils.subtract(c2, c1, t);
|
|
|
|
var distsq = t.lengthSq();
|
|
|
|
if (distsq > rmax * rmax)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var dist = Math.sqrt(distsq);
|
|
|
|
var p: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.multiplyAdd(c1, t, 0.5 + (r1 - r2) * 0.5 / dist, p);
|
|
//var p = vec2.mad(c1, t, 0.5 + (r1 - r2) * 0.5 / dist);
|
|
|
|
var n: Phaser.Vec2 = new Phaser.Vec2;
|
|
//var n = (dist != 0) ? vec2.scale(t, 1 / dist) : vec2.zero;
|
|
|
|
if (dist != 0)
|
|
{
|
|
Phaser.Vec2Utils.scale(t, 1 / dist, n);
|
|
}
|
|
|
|
var d = dist - rmax;
|
|
|
|
contactArr.push(new Contact(p, n, d, 0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
public circle2Circle(circ1: Phaser.Physics.Shapes.Circle, circ2: Phaser.Physics.Shapes.Circle, contactArr: Contact[]) {
|
|
return this._circle2Circle(circ1.tc, circ1.radius, circ2.tc, circ2.radius, contactArr);
|
|
}
|
|
|
|
public circle2Segment(circ: Phaser.Physics.Shapes.Circle, seg: Phaser.Physics.Shapes.Segment, contactArr: Contact[]) {
|
|
|
|
var rsum = circ.radius + seg.radius;
|
|
|
|
// Normal distance from segment
|
|
var dn = Phaser.Vec2Utils.dot(circ.tc, seg.tn) - Phaser.Vec2Utils.dot(seg.ta, seg.tn);
|
|
var dist = (dn < 0 ? dn * -1 : dn) - rsum;
|
|
if (dist > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Tangential distance along segment
|
|
var dt = Phaser.Vec2Utils.cross(circ.tc, seg.tn);
|
|
var dtMin = Phaser.Vec2Utils.cross(seg.ta, seg.tn);
|
|
var dtMax = Phaser.Vec2Utils.cross(seg.tb, seg.tn);
|
|
|
|
if (dt < dtMin)
|
|
{
|
|
if (dt < dtMin - rsum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return this._circle2Circle(circ.tc, circ.radius, seg.ta, seg.radius, contactArr);
|
|
}
|
|
else if (dt > dtMax)
|
|
{
|
|
if (dt > dtMax + rsum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return this._circle2Circle(circ.tc, circ.radius, seg.tb, seg.radius, contactArr);
|
|
}
|
|
|
|
var n: Phaser.Vec2 = new Phaser.Vec2;
|
|
|
|
if (dn > 0)
|
|
{
|
|
n.copyFrom(seg.tn);
|
|
}
|
|
else
|
|
{
|
|
Phaser.Vec2Utils.negative(seg.tn, n);
|
|
}
|
|
//var n = (dn > 0) ? seg.tn : vec2.neg(seg.tn);
|
|
|
|
var c1: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.multiplyAdd(circ.tc, n, -(circ.radius + dist * 0.5), c1);
|
|
|
|
var c2: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.negative(n, c2);
|
|
|
|
contactArr.push(new Contact(c1, c2, dist, 0));
|
|
//contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + dist * 0.5)), vec2.neg(n), dist, 0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
public circle2Poly(circ: Phaser.Physics.Shapes.Circle, poly: Phaser.Physics.Shapes.Poly, contactArr: Contact[]) {
|
|
|
|
var minDist = -999999;
|
|
var minIdx = -1;
|
|
|
|
for (var i = 0; i < poly.verts.length; i++)
|
|
{
|
|
var plane = poly.tplanes[i];
|
|
var dist = Phaser.Vec2Utils.dot(circ.tc, plane.normal) - plane.d - circ.radius;
|
|
|
|
if (dist > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (dist > minDist)
|
|
{
|
|
minDist = dist;
|
|
minIdx = i;
|
|
}
|
|
}
|
|
|
|
var n = poly.tplanes[minIdx].normal;
|
|
var a = poly.tverts[minIdx];
|
|
var b = poly.tverts[(minIdx + 1) % poly.verts.length];
|
|
var dta = Phaser.Vec2Utils.cross(a, n);
|
|
var dtb = Phaser.Vec2Utils.cross(b, n);
|
|
var dt = Phaser.Vec2Utils.cross(circ.tc, n);
|
|
|
|
if (dt > dta)
|
|
{
|
|
return this._circle2Circle(circ.tc, circ.radius, a, 0, contactArr);
|
|
}
|
|
else if (dt < dtb)
|
|
{
|
|
return this._circle2Circle(circ.tc, circ.radius, b, 0, contactArr);
|
|
}
|
|
|
|
var c1: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.multiplyAdd(circ.tc, n, -(circ.radius + minDist * 0.5), c1);
|
|
|
|
var c2: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.negative(n, c2);
|
|
|
|
contactArr.push(new Contact(c1, c2, minDist, 0));
|
|
|
|
//contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + minDist * 0.5)), vec2.neg(n), minDist, 0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
public segmentPointDistanceSq(seg: Phaser.Physics.Shapes.Segment, p) {
|
|
|
|
var w: Phaser.Vec2 = new Phaser.Vec2;
|
|
var d: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.subtract(p, seg.ta, w);
|
|
Phaser.Vec2Utils.subtract(seg.tb, seg.ta, d);
|
|
|
|
//var w = vec2.sub(p, seg.ta);
|
|
//var d = vec2.sub(seg.tb, seg.ta);
|
|
|
|
var proj = w.dot(d);
|
|
|
|
if (proj <= 0)
|
|
{
|
|
return w.dot(w);
|
|
}
|
|
|
|
var vsq = d.dot(d);
|
|
|
|
if (proj >= vsq)
|
|
{
|
|
return w.dot(w) - 2 * proj + vsq;
|
|
}
|
|
|
|
return w.dot(w) - proj * proj / vsq;
|
|
|
|
}
|
|
|
|
// FIXME and optimise me lots!!!
|
|
public segment2Segment(seg1: Phaser.Physics.Shapes.Segment, seg2: Phaser.Physics.Shapes.Segment, contactArr: Contact[]) {
|
|
|
|
var d = [];
|
|
d[0] = this.segmentPointDistanceSq(seg1, seg2.ta);
|
|
d[1] = this.segmentPointDistanceSq(seg1, seg2.tb);
|
|
d[2] = this.segmentPointDistanceSq(seg2, seg1.ta);
|
|
d[3] = this.segmentPointDistanceSq(seg2, seg1.tb);
|
|
|
|
var idx1 = d[0] < d[1] ? 0 : 1;
|
|
var idx2 = d[2] < d[3] ? 2 : 3;
|
|
var idxm = d[idx1] < d[idx2] ? idx1 : idx2;
|
|
var s, t;
|
|
|
|
var u = Phaser.Vec2Utils.subtract(seg1.tb, seg1.ta);
|
|
var v = Phaser.Vec2Utils.subtract(seg2.tb, seg2.ta);
|
|
|
|
switch (idxm)
|
|
{
|
|
case 0:
|
|
s = Phaser.Vec2Utils.dot(Phaser.Vec2Utils.subtract(seg2.ta, seg1.ta), u) / Phaser.Vec2Utils.dot(u, u);
|
|
s = s < 0 ? 0 : (s > 1 ? 1 : s);
|
|
t = 0;
|
|
break;
|
|
case 1:
|
|
s = Phaser.Vec2Utils.dot(Phaser.Vec2Utils.subtract(seg2.tb, seg1.ta), u) / Phaser.Vec2Utils.dot(u, u);
|
|
s = s < 0 ? 0 : (s > 1 ? 1 : s);
|
|
t = 1;
|
|
break;
|
|
case 2:
|
|
s = 0;
|
|
t = Phaser.Vec2Utils.dot(Phaser.Vec2Utils.subtract(seg1.ta, seg2.ta), v) / Phaser.Vec2Utils.dot(v, v);
|
|
t = t < 0 ? 0 : (t > 1 ? 1 : t);
|
|
break;
|
|
case 3:
|
|
s = 1;
|
|
t = Phaser.Vec2Utils.dot(Phaser.Vec2Utils.subtract(seg1.tb, seg2.ta), v) / Phaser.Vec2Utils.dot(v, v);
|
|
t = t < 0 ? 0 : (t > 1 ? 1 : t);
|
|
break;
|
|
}
|
|
|
|
var minp1 = Phaser.Vec2Utils.multiplyAdd(seg1.ta, u, s);
|
|
var minp2 = Phaser.Vec2Utils.multiplyAdd(seg2.ta, v, t);
|
|
|
|
return this._circle2Circle(minp1, seg1.radius, minp2, seg2.radius, contactArr);
|
|
|
|
}
|
|
|
|
// Identify vertexes that have penetrated the segment.
|
|
public findPointsBehindSeg(contactArr: Contact[], seg: Phaser.Physics.Shapes.Segment, poly: Phaser.Physics.Shapes.Poly, dist: number, coef: number) {
|
|
|
|
var dta = Phaser.Vec2Utils.cross(seg.tn, seg.ta);
|
|
var dtb = Phaser.Vec2Utils.cross(seg.tn, seg.tb);
|
|
|
|
var n: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.scale(seg.tn, coef, n);
|
|
//var n = vec2.scale(seg.tn, coef);
|
|
|
|
for (var i = 0; i < poly.verts.length; i++)
|
|
{
|
|
var v = poly.tverts[i];
|
|
|
|
if (Phaser.Vec2Utils.dot(v, n) < Phaser.Vec2Utils.dot(seg.tn, seg.ta) * coef + seg.radius)
|
|
{
|
|
var dt = Phaser.Vec2Utils.cross(seg.tn, v);
|
|
|
|
if (dta >= dt && dt >= dtb)
|
|
{
|
|
contactArr.push(new Contact(v, n, dist, (poly.id << 16) | i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public segment2Poly(seg: Phaser.Physics.Shapes.Segment, poly: Phaser.Physics.Shapes.Poly, contactArr: Contact[]) {
|
|
|
|
var seg_td = Phaser.Vec2Utils.dot(seg.tn, seg.ta);
|
|
var seg_d1 = poly.distanceOnPlane(seg.tn, seg_td) - seg.radius;
|
|
|
|
if (seg_d1 > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var n: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.negative(seg.tn, n);
|
|
var seg_d2 = poly.distanceOnPlane(n, -seg_td) - seg.radius;
|
|
//var seg_d2 = poly.distanceOnPlane(vec2.neg(seg.tn), -seg_td) - seg.r;
|
|
|
|
if (seg_d2 > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var poly_d = -999999;
|
|
var poly_i = -1;
|
|
|
|
for (var i = 0; i < poly.verts.length; i++)
|
|
{
|
|
var plane = poly.tplanes[i];
|
|
var dist = seg.distanceOnPlane(plane.normal, plane.d);
|
|
|
|
if (dist > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (dist > poly_d)
|
|
{
|
|
poly_d = dist;
|
|
poly_i = i;
|
|
}
|
|
}
|
|
|
|
var poly_n: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.negative(poly.tplanes[poly_i].normal, poly_n);
|
|
//var poly_n = vec2.neg(poly.tplanes[poly_i].n);
|
|
|
|
var va: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.multiplyAdd(seg.ta, poly_n, seg.radius, va);
|
|
//var va = vec2.mad(seg.ta, poly_n, seg.r);
|
|
|
|
var vb: Phaser.Vec2 = new Phaser.Vec2;
|
|
Phaser.Vec2Utils.multiplyAdd(seg.tb, poly_n, seg.radius, vb);
|
|
//var vb = vec2.mad(seg.tb, poly_n, seg.r);
|
|
|
|
if (poly.containPoint(va))
|
|
{
|
|
contactArr.push(new Contact(va, poly_n, poly_d, (seg.id << 16) | 0));
|
|
}
|
|
|
|
if (poly.containPoint(vb))
|
|
{
|
|
contactArr.push(new Contact(vb, poly_n, poly_d, (seg.id << 16) | 1));
|
|
}
|
|
|
|
// Floating point precision problems here.
|
|
// This will have to do for now.
|
|
poly_d -= 0.1
|
|
|
|
if (seg_d1 >= poly_d || seg_d2 >= poly_d)
|
|
{
|
|
if (seg_d1 > seg_d2)
|
|
{
|
|
this.findPointsBehindSeg(contactArr, seg, poly, seg_d1, 1);
|
|
}
|
|
else
|
|
{
|
|
this.findPointsBehindSeg(contactArr, seg, poly, seg_d2, -1);
|
|
}
|
|
}
|
|
|
|
// If no other collision points are found, try colliding endpoints.
|
|
if (contactArr.length == 0)
|
|
{
|
|
var poly_a = poly.tverts[poly_i];
|
|
var poly_b = poly.tverts[(poly_i + 1) % poly.verts.length];
|
|
|
|
if (this._circle2Circle(seg.ta, seg.radius, poly_a, 0, contactArr))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (this._circle2Circle(seg.tb, seg.radius, poly_a, 0, contactArr))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (this._circle2Circle(seg.ta, seg.radius, poly_b, 0, contactArr))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (this._circle2Circle(seg.tb, seg.radius, poly_b, 0, contactArr))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return contactArr.length;
|
|
|
|
}
|
|
|
|
// Find the minimum separating axis for the given poly and plane list.
|
|
public findMSA(poly: Phaser.Physics.Shapes.Poly, planes: Phaser.Physics.Plane[], num: number) {
|
|
|
|
var min_dist: number = -999999;
|
|
var min_index: number = -1;
|
|
|
|
for (var i: number = 0; i < num; i++)
|
|
{
|
|
var dist: number = poly.distanceOnPlane(planes[i].normal, planes[i].d);
|
|
|
|
if (dist > 0)
|
|
{
|
|
// no collision
|
|
return { dist: 0, index: -1 };
|
|
}
|
|
else if (dist > min_dist)
|
|
{
|
|
min_dist = dist;
|
|
min_index = i;
|
|
}
|
|
}
|
|
|
|
// new object - see what we can do here
|
|
return { dist: min_dist, index: min_index };
|
|
|
|
}
|
|
|
|
public findVertsFallback(contactArr: Contact[], poly1: Phaser.Physics.Shapes.Poly, poly2: Phaser.Physics.Shapes.Poly, n, dist: number) {
|
|
|
|
var num = 0;
|
|
|
|
for (var i = 0; i < poly1.verts.length; i++)
|
|
{
|
|
var v = poly1.tverts[i];
|
|
|
|
if (poly2.containPointPartial(v, n))
|
|
{
|
|
contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
|
|
num++;
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < poly2.verts.length; i++)
|
|
{
|
|
var v = poly2.tverts[i];
|
|
|
|
if (poly1.containPointPartial(v, n))
|
|
{
|
|
contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
// Find the overlapped vertices.
|
|
public findVerts(contactArr: Contact[], poly1: Phaser.Physics.Shapes.Poly, poly2: Phaser.Physics.Shapes.Poly, n, dist: number) {
|
|
|
|
var num = 0;
|
|
|
|
for (var i = 0; i < poly1.verts.length; i++)
|
|
{
|
|
var v = poly1.tverts[i];
|
|
|
|
if (poly2.containPoint(v))
|
|
{
|
|
contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
|
|
num++;
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < poly2.verts.length; i++)
|
|
{
|
|
var v = poly2.tverts[i];
|
|
|
|
if (poly1.containPoint(v))
|
|
{
|
|
contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num > 0 ? num : this.findVertsFallback(contactArr, poly1, poly2, n, dist);
|
|
|
|
}
|
|
|
|
public poly2Poly(poly1: Phaser.Physics.Shapes.Poly, poly2: Phaser.Physics.Shapes.Poly, contactArr: Contact[]) {
|
|
|
|
var msa1 = this.findMSA(poly2, poly1.tplanes, poly1.verts.length);
|
|
|
|
if (msa1.index == -1)
|
|
{
|
|
console.log('poly2poly 0', msa1);
|
|
return 0;
|
|
}
|
|
|
|
var msa2 = this.findMSA(poly1, poly2.tplanes, poly2.verts.length);
|
|
|
|
if (msa2.index == -1)
|
|
{
|
|
console.log('poly2poly 1', msa2);
|
|
return 0;
|
|
}
|
|
|
|
// Penetration normal direction should be from poly1 to poly2
|
|
if (msa1.dist > msa2.dist)
|
|
{
|
|
return this.findVerts(contactArr, poly1, poly2, poly1.tplanes[msa1.index].normal, msa1.dist);
|
|
}
|
|
|
|
return this.findVerts(contactArr, poly1, poly2, Phaser.Vec2Utils.negative(poly2.tplanes[msa2.index].normal), msa2.dist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} |