mirror of
https://github.com/MapMakersAndProgrammers/alternativaphysics-archive.git
synced 2025-10-26 09:59:12 -07:00
576 lines
22 KiB
Plaintext
576 lines
22 KiB
Plaintext
package alternativa.physics.collision.colliders {
|
||
import alternativa.physics.Contact;
|
||
import alternativa.physics.ContactPoint;
|
||
import alternativa.physics.altphysics;
|
||
import alternativa.physics.collision.CollisionPrimitive;
|
||
import alternativa.physics.collision.primitives.CollisionBox;
|
||
import alternativa.physics.collision.primitives.CollisionRect;
|
||
import alternativa.math.Matrix4;
|
||
import alternativa.math.Vector3;
|
||
use namespace altphysics;
|
||
|
||
/**
|
||
*
|
||
*/
|
||
public class BoxRectCollider extends BoxCollider {
|
||
|
||
private var epsilon:Number = 0.001;
|
||
|
||
private var vectorToBox:Vector3 = new Vector3();
|
||
private var axis:Vector3 = new Vector3();
|
||
private var axis10:Vector3 = new Vector3();
|
||
private var axis11:Vector3 = new Vector3();
|
||
private var axis12:Vector3 = new Vector3();
|
||
private var axis20:Vector3 = new Vector3();
|
||
private var axis21:Vector3 = new Vector3();
|
||
private var axis22:Vector3 = new Vector3();
|
||
|
||
private var bestAxisIndex:int;
|
||
private var minOverlap:Number;
|
||
|
||
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||
|
||
/**
|
||
*
|
||
*/
|
||
public function BoxRectCollider() {
|
||
for (var i:int = 0; i < 8; i++) {
|
||
points1[i] = new Vector3();
|
||
points2[i] = new Vector3();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||
*
|
||
* @param prim1 первый примитив
|
||
* @param prim2 второй примитив
|
||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||
* @return true, если пересечение существует, иначе false
|
||
*/
|
||
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||
if (!haveCollision(prim1, prim2)) return false;
|
||
|
||
var box:CollisionBox = prim1 as CollisionBox;
|
||
var rect:CollisionRect;
|
||
if (box == null) {
|
||
box = prim2 as CollisionBox;
|
||
rect = prim1 as CollisionRect;
|
||
} else {
|
||
rect = prim2 as CollisionRect;
|
||
}
|
||
|
||
if (bestAxisIndex < 4) {
|
||
// Контакт вдоль одной из основных осей
|
||
if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
|
||
} else {
|
||
// Контакт ребро-ребро
|
||
bestAxisIndex -= 4;
|
||
if (!findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact)) {
|
||
return false;
|
||
}
|
||
}
|
||
contact.body1 = box.body;
|
||
contact.body2 = rect.body;
|
||
|
||
// Хак для танков, чтобы исключить утыкания танков в стыки статических примитивов
|
||
if (rect.transform.k > 0.99999) {
|
||
contact.normal.x = 0;
|
||
contact.normal.y = 0;
|
||
contact.normal.z = 1;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||
*
|
||
* @param prim1 первый примитив
|
||
* @param prim2 второй примитив
|
||
* @return true, если пересечение существует, иначе false
|
||
*/
|
||
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||
minOverlap = 1e10;
|
||
var box:CollisionBox = prim1 as CollisionBox;
|
||
var rect:CollisionRect;
|
||
if (box == null) {
|
||
box = prim2 as CollisionBox;
|
||
rect = prim1 as CollisionRect;
|
||
} else {
|
||
rect = prim2 as CollisionRect;
|
||
}
|
||
var boxTransform:Matrix4 = box.transform;
|
||
var rectTransform:Matrix4 = rect.transform;
|
||
|
||
// Вектор из центра прямоугольника в центр бокса
|
||
vectorToBox.x = boxTransform.d - rectTransform.d;
|
||
vectorToBox.y = boxTransform.h - rectTransform.h;
|
||
vectorToBox.z = boxTransform.l - rectTransform.l;
|
||
|
||
// Проверка пересечения по нормали прямоугольника
|
||
rectTransform.getAxis(2, axis22);
|
||
if (!testMainAxis(box, rect, axis22, 0, vectorToBox)) return false;
|
||
|
||
// Проверка пересечения по основным осям бокса
|
||
boxTransform.getAxis(0, axis10);
|
||
if (!testMainAxis(box, rect, axis10, 1, vectorToBox)) return false;
|
||
boxTransform.getAxis(1, axis11);
|
||
if (!testMainAxis(box, rect, axis11, 2, vectorToBox)) return false;
|
||
boxTransform.getAxis(2, axis12);
|
||
if (!testMainAxis(box, rect, axis12, 3, vectorToBox)) return false;
|
||
|
||
// Получаем направляющие рёбер прямоугольника
|
||
rectTransform.getAxis(0, axis20);
|
||
rectTransform.getAxis(1, axis21);
|
||
|
||
// Проверка производных осей
|
||
if (!testDerivedAxis(box, rect, axis10, axis20, 4, vectorToBox)) return false;
|
||
if (!testDerivedAxis(box, rect, axis10, axis21, 5, vectorToBox)) return false;
|
||
|
||
if (!testDerivedAxis(box, rect, axis11, axis20, 6, vectorToBox)) return false;
|
||
if (!testDerivedAxis(box, rect, axis11, axis21, 7, vectorToBox)) return false;
|
||
|
||
if (!testDerivedAxis(box, rect, axis12, axis20, 8, vectorToBox)) return false;
|
||
if (!testDerivedAxis(box, rect, axis12, axis21, 9, vectorToBox)) return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Выполняет поиск точек контакта бокса с прямоугольником.
|
||
*
|
||
* @param box бокс
|
||
* @param rect прямоугольник
|
||
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
|
||
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
|
||
* @param colInfo структура, в которую записывается информация о точках контакта
|
||
*/
|
||
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
|
||
var pnum:int;
|
||
var i:int;
|
||
var v:Vector3;
|
||
var cp:ContactPoint;
|
||
var boxTransform:Matrix4 = box.transform;
|
||
var rectTransform:Matrix4 = rect.transform;
|
||
var colAxis:Vector3 = contact.normal;
|
||
|
||
var negativeFace:Boolean;
|
||
var code:int;
|
||
|
||
if (faceAxisIdx == 0) {
|
||
// Столкновение с плоскостью прямоугольника
|
||
|
||
// Проверим положение бокса относительно плоскости прямоугольника
|
||
colAxis.x = rectTransform.c;
|
||
colAxis.y = rectTransform.g;
|
||
colAxis.z = rectTransform.k;
|
||
|
||
// var offset:Number = colAxis.x*rectTransform.d + colAxis.y*rectTransform.h + colAxis.z*rectTransform.l;
|
||
// if (bbPos.vDot(colAxis) < offset) return false;
|
||
|
||
// Ищем ось бокса, определяющую наиболее антипараллельную грань
|
||
var incidentAxisIdx:int = 0;
|
||
var incidentAxisDot:Number;
|
||
var maxDot:Number = 0;
|
||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||
boxTransform.getAxis(axisIdx, axis);
|
||
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
|
||
var absDot:Number = dot < 0 ? -dot : dot;
|
||
if (absDot > maxDot) {
|
||
maxDot = absDot;
|
||
incidentAxisIdx = axisIdx;
|
||
incidentAxisDot = dot;
|
||
}
|
||
}
|
||
negativeFace = incidentAxisDot > 0;
|
||
|
||
code = 1 << (incidentAxisIdx << 1);
|
||
if (negativeFace) {
|
||
code <<= 1;
|
||
}
|
||
if ((code & box.excludedFaces) != 0) return false;
|
||
|
||
|
||
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
|
||
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
|
||
boxTransform.getAxis(incidentAxisIdx, axis);
|
||
getFaceVertsByAxis(box.hs, incidentAxisIdx, negativeFace, points1);
|
||
boxTransform.transformVectorsN(points1, points2, 4);
|
||
rectTransform.transformVectorsInverseN(points2, points1, 4);
|
||
pnum = clipByRect(rect.hs);
|
||
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
|
||
contact.pcount = 0;
|
||
for (i = 0; i < pnum; i++) {
|
||
v = points1[i];
|
||
if (v.z < epsilon) {
|
||
cp = contact.points[contact.pcount++];
|
||
cp.penetration = -v.z;
|
||
var cpPos:Vector3 = cp.pos;
|
||
cpPos.x = rectTransform.a*v.x + rectTransform.b*v.y + rectTransform.c*v.z + rectTransform.d;
|
||
cpPos.y = rectTransform.e*v.x + rectTransform.f*v.y + rectTransform.g*v.z + rectTransform.h;
|
||
cpPos.z = rectTransform.i*v.x + rectTransform.j*v.y + rectTransform.k*v.z + rectTransform.l;
|
||
v = cp.r1;
|
||
v.x = cpPos.x - boxTransform.d;
|
||
v.y = cpPos.y - boxTransform.h;
|
||
v.z = cpPos.z - boxTransform.l;
|
||
v = cp.r2;
|
||
v.x = cpPos.x - rectTransform.d;
|
||
v.y = cpPos.y - rectTransform.h;
|
||
v.z = cpPos.z - rectTransform.l;
|
||
}
|
||
}
|
||
} else {
|
||
// Столкновение с гранью бокса
|
||
faceAxisIdx--;
|
||
boxTransform.getAxis(faceAxisIdx, colAxis);
|
||
negativeFace = colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z > 0;
|
||
|
||
code = 1 << (faceAxisIdx << 1);
|
||
if (negativeFace) {
|
||
code <<= 1;
|
||
}
|
||
if ((code & box.excludedFaces) != 0) {
|
||
return false;
|
||
}
|
||
|
||
if (!negativeFace) {
|
||
colAxis.x = -colAxis.x;
|
||
colAxis.y = -colAxis.y;
|
||
colAxis.z = -colAxis.z;
|
||
}
|
||
|
||
if (rectTransform.c*colAxis.x + rectTransform.g*colAxis.y + rectTransform.k*colAxis.z < 0) return false;
|
||
|
||
getFaceVertsByAxis(rect.hs, 2, false, points1);
|
||
rectTransform.transformVectorsN(points1, points2, 4);
|
||
boxTransform.transformVectorsInverseN(points2, points1, 4);
|
||
pnum = clipByBox(box.hs, faceAxisIdx);
|
||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||
var pen:Number;
|
||
contact.pcount = 0;
|
||
for (i = 0; i < pnum; i++) {
|
||
v = points1[i];
|
||
if ((pen = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
|
||
cp = contact.points[contact.pcount++];
|
||
cp.penetration = pen;
|
||
cpPos = cp.pos;
|
||
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
|
||
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
|
||
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
|
||
v = cp.r1;
|
||
v.x = cpPos.x - boxTransform.d;
|
||
v.y = cpPos.y - boxTransform.h;
|
||
v.z = cpPos.z - boxTransform.l;
|
||
v = cp.r2;
|
||
v.x = cpPos.x - rectTransform.d;
|
||
v.y = cpPos.y - rectTransform.h;
|
||
v.z = cpPos.z - rectTransform.l;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param hs
|
||
* @param p
|
||
* @param axisIndex
|
||
* @param reverse
|
||
* @return
|
||
*/
|
||
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
|
||
switch (faceAxisIdx) {
|
||
case 0:
|
||
if (reverse) return p.x + hs.x;
|
||
else return hs.x - p.x;
|
||
case 1:
|
||
if (reverse) return p.y + hs.y;
|
||
else return hs.y - p.y;
|
||
case 2:
|
||
if (reverse) return p.z + hs.z;
|
||
else return hs.z - p.z;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
|
||
*
|
||
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
|
||
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
|
||
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
|
||
*/
|
||
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
|
||
var pnum:int = 4;
|
||
switch (faceAxisIdx) {
|
||
case 0:
|
||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||
case 1:
|
||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
return clipHighX(hs.x, pnum, points2, points1, epsilon);
|
||
case 2:
|
||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param hs
|
||
* @return
|
||
*/
|
||
private function clipByRect(hs:Vector3):int {
|
||
var pnum:int = 4;
|
||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||
}
|
||
|
||
/**
|
||
* Вычисляет точку столкновения рёбер двух боксов.
|
||
*
|
||
* @param box первый бокс
|
||
* @param rect второй бокс
|
||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||
* @param axisIdx1 индекс направляющей оси ребра первого бокса
|
||
* @param axisIdx2 индекс направляющей оси ребра второго бокса
|
||
* @param colInfo структура, в которую записывается информация о столкновении
|
||
*/
|
||
private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):Boolean {
|
||
var boxTransform:Matrix4 = box.transform;
|
||
var rectTransform:Matrix4 = rect.transform;
|
||
|
||
boxTransform.getAxis(axisIdx1, axis10);
|
||
rectTransform.getAxis(axisIdx2, axis20);
|
||
var colAxis:Vector3 = contact.normal;
|
||
colAxis.x = axis10.y*axis20.z - axis10.z*axis20.y;
|
||
colAxis.y = axis10.z*axis20.x - axis10.x*axis20.z;
|
||
colAxis.z = axis10.x*axis20.y - axis10.y*axis20.x;
|
||
var k:Number = 1/Math.sqrt(colAxis.x*colAxis.x + colAxis.y*colAxis.y + colAxis.z*colAxis.z);
|
||
colAxis.x *= k;
|
||
colAxis.y *= k;
|
||
colAxis.z *= k;
|
||
|
||
// Разворот оси в сторону бокса
|
||
if (colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z < 0) {
|
||
colAxis.x = -colAxis.x;
|
||
colAxis.y = -colAxis.y;
|
||
colAxis.z = -colAxis.z;
|
||
}
|
||
|
||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||
var halfLen1:Number;
|
||
var halfLen2:Number;
|
||
var vx:Number = box.hs.x;
|
||
var vy:Number = box.hs.y;
|
||
var vz:Number = box.hs.z;
|
||
var x2:Number = rect.hs.x;
|
||
var y2:Number = rect.hs.y;
|
||
var z2:Number = rect.hs.z;
|
||
// x
|
||
if (axisIdx1 == 0) {
|
||
vx = 0;
|
||
halfLen1 = box.hs.x;
|
||
} else {
|
||
if (boxTransform.a*colAxis.x + boxTransform.e*colAxis.y + boxTransform.i*colAxis.z > 0) {
|
||
vx = -vx;
|
||
if ((box.excludedFaces & 2) != 0) {
|
||
return false;
|
||
}
|
||
} else {
|
||
if ((box.excludedFaces & 1) != 0) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
if (axisIdx2 == 0) {
|
||
x2 = 0;
|
||
halfLen2 = rect.hs.x;
|
||
} else {
|
||
if (rectTransform.a*colAxis.x + rectTransform.e*colAxis.y + rectTransform.i*colAxis.z < 0) {
|
||
x2 = -x2;
|
||
}
|
||
}
|
||
// y
|
||
if (axisIdx1 == 1) {
|
||
vy = 0;
|
||
halfLen1 = box.hs.y;
|
||
} else {
|
||
if (boxTransform.b*colAxis.x + boxTransform.f*colAxis.y + boxTransform.j*colAxis.z > 0) {
|
||
vy = -vy;
|
||
if ((box.excludedFaces & 8) != 0) {
|
||
return false;
|
||
}
|
||
} else {
|
||
if ((box.excludedFaces & 4) != 0) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
if (axisIdx2 == 1) {
|
||
y2 = 0;
|
||
halfLen2 = rect.hs.y;
|
||
} else {
|
||
if (rectTransform.b*colAxis.x + rectTransform.f*colAxis.y + rectTransform.j*colAxis.z < 0) {
|
||
y2 = -y2;
|
||
}
|
||
}
|
||
// z
|
||
if (axisIdx1 == 2) {
|
||
vz = 0;
|
||
halfLen1 = box.hs.z;
|
||
} else {
|
||
if (boxTransform.c*colAxis.x + boxTransform.g*colAxis.y + boxTransform.k*colAxis.z > 0) {
|
||
vz = -vz;
|
||
if ((box.excludedFaces & 32) != 0) {
|
||
return false;
|
||
}
|
||
} else {
|
||
if ((box.excludedFaces & 16) != 0) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
// Получаем глобальные координаты средних точек рёбер
|
||
|
||
var x1:Number = boxTransform.a*vx + boxTransform.b*vy + boxTransform.c*vz + boxTransform.d;
|
||
var y1:Number = boxTransform.e*vx + boxTransform.f*vy + boxTransform.g*vz + boxTransform.h;
|
||
var z1:Number = boxTransform.i*vx + boxTransform.j*vy + boxTransform.k*vz + boxTransform.l;
|
||
vx = x2;
|
||
vy = y2;
|
||
vz = z2;
|
||
x2 = rectTransform.a*vx + rectTransform.b*vy + rectTransform.c*vz + rectTransform.d;
|
||
y2 = rectTransform.e*vx + rectTransform.f*vy + rectTransform.g*vz + rectTransform.h;
|
||
z2 = rectTransform.i*vx + rectTransform.j*vy + rectTransform.k*vz + rectTransform.l;
|
||
// Находим точку пересечения рёбер, решая систему уравнений
|
||
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
|
||
var det:Number = k*k - 1;
|
||
vx = x2 - x1;
|
||
vy = y2 - y1;
|
||
vz = z2 - z1;
|
||
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
|
||
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
|
||
var t1:Number = (c2*k - c1)/det;
|
||
var t2:Number = (c2 - c1*k)/det;
|
||
// Запись данных о столкновении
|
||
contact.pcount = 1;
|
||
var cp:ContactPoint = contact.points[0];
|
||
cp.penetration = minOverlap;
|
||
var cpPos:Vector3 = cp.pos;
|
||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
|
||
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
|
||
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
|
||
var v:Vector3 = cp.r1;
|
||
v.x = cpPos.x - boxTransform.d;
|
||
v.y = cpPos.y - boxTransform.h;
|
||
v.z = cpPos.z - boxTransform.l;
|
||
v = cp.r2;
|
||
v.x = cpPos.x - rectTransform.d;
|
||
v.y = cpPos.y - rectTransform.h;
|
||
v.z = cpPos.z - rectTransform.l;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||
*
|
||
* @param box
|
||
* @param rect
|
||
* @param axis
|
||
* @param axisIndex
|
||
* @param vectorToBox
|
||
* @param bestAxis
|
||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||
*/
|
||
private function testMainAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||
if (overlap < -epsilon) return false;
|
||
if (overlap + epsilon < minOverlap) {
|
||
minOverlap = overlap;
|
||
bestAxisIndex = axisIndex;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param box
|
||
* @param rect
|
||
* @param axis1
|
||
* @param axis2
|
||
* @param axisIndex
|
||
* @param vectorToBox
|
||
* @return
|
||
*
|
||
*/
|
||
private function testDerivedAxis(box:CollisionBox, rect:CollisionRect, axis1:Vector3, axis2:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||
// axis = axis1 cross axis2
|
||
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
|
||
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
|
||
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
|
||
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
|
||
if (lenSqr < 0.0001) return true;
|
||
var k:Number = 1/Math.sqrt(lenSqr);
|
||
axis.x *= k;
|
||
axis.y *= k;
|
||
axis.z *= k;
|
||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||
if (overlap < -epsilon) return false;
|
||
if (overlap + epsilon < minOverlap) {
|
||
minOverlap = overlap;
|
||
bestAxisIndex = axisIndex;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Вычисляет глубину перекрытия вдоль заданной оси.
|
||
*
|
||
* @param box бокс
|
||
* @param rect прямоугольник
|
||
* @param axis ось
|
||
* @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
|
||
* @return величина перекрытия вдоль оси
|
||
*/
|
||
public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
|
||
var m:Matrix4 = box.transform;
|
||
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
|
||
if (d < 0) d = -d;
|
||
var projection:Number = d;
|
||
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
|
||
if (d < 0) d = -d;
|
||
projection += d;
|
||
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
|
||
if (d < 0) d = -d;
|
||
projection += d;
|
||
|
||
m = rect.transform;
|
||
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
|
||
if (d < 0) d = -d;
|
||
projection += d;
|
||
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
|
||
if (d < 0) d = -d;
|
||
projection += d;
|
||
|
||
d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
|
||
if (d < 0) d = -d;
|
||
|
||
return projection - d;
|
||
}
|
||
|
||
}
|
||
}
|