everything i found in leaked code

This commit is contained in:
Tubix
2024-10-05 12:31:02 +01:00
parent 6679f44200
commit 28441faac3
2960 changed files with 394479 additions and 0 deletions

View File

@@ -0,0 +1,568 @@
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.physics.math.Matrix4;
import alternativa.physics.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;
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;
}
}
}