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,589 @@
package alternativa.physics.collision.colliders {
import __AS3__.vec.Vector;
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.math.Matrix4;
import alternativa.physics.math.Vector3;
use namespace altphysics;
/**
* Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
*/
public class BoxBoxCollider extends BoxCollider {
private var epsilon:Number = 0.001;
private var vectorToBox1: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);
private var tmpPoints:Vector.<ContactPoint> = new Vector.<ContactPoint>(8, true);
private var pcount:int;
/**
*
*/
public function BoxBoxCollider() {
for (var i:int = 0; i < 8; i++) {
tmpPoints[i] = new ContactPoint();
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 box1:CollisionBox;
var box2:CollisionBox;
if (prim1.body != null) {
box1 = prim1 as CollisionBox;
box2 = prim2 as CollisionBox;
} else {
box1 = prim2 as CollisionBox;
box2 = prim1 as CollisionBox;
}
if (bestAxisIndex < 6) {
// Контакт грань-(грань|ребро|вершина)
if (!findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact)) return false;
} else {
// Контакт ребро-ребро
bestAxisIndex -= 6;
findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
}
contact.body1 = box1.body;
contact.body2 = box2.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
minOverlap = 1e10;
var box1:CollisionBox;
var box2:CollisionBox;
if (prim1.body != null) {
box1 = prim1 as CollisionBox;
box2 = prim2 as CollisionBox;
} else {
box1 = prim2 as CollisionBox;
box2 = prim1 as CollisionBox;
}
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
// Вектор из центра второго бокса в центр первого
vectorToBox1.x = transform1.d - transform2.d;
vectorToBox1.y = transform1.h - transform2.h;
vectorToBox1.z = transform1.l - transform2.l;
// Проверка пересечения по основным осям
axis10.x = transform1.a;
axis10.y = transform1.e;
axis10.z = transform1.i;
if (!testMainAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
axis11.x = transform1.b;
axis11.y = transform1.f;
axis11.z = transform1.j;
if (!testMainAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
axis12.x = transform1.c;
axis12.y = transform1.g;
axis12.z = transform1.k;
if (!testMainAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
axis20.x = transform2.a;
axis20.y = transform2.e;
axis20.z = transform2.i;
if (!testMainAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
axis21.x = transform2.b;
axis21.y = transform2.f;
axis21.z = transform2.j;
if (!testMainAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
axis22.x = transform2.c;
axis22.y = transform2.g;
axis22.z = transform2.k;
if (!testMainAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
// Проверка производных осей
if (!testDerivedAxis(box1, box2, axis10, axis20, 6, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis21, 7, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis22, 8, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis20, 9, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis21, 10, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis22, 11, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis20, 12, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis21, 13, vectorToBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis22, 14, vectorToBox1)) return false;
return true;
}
/**
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
* @param contactInfo структура, в которую записывается информация о точках контакта
*/
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
var swapNormal:Boolean = false;
if (faceAxisIdx > 2) {
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
var tmpBox:CollisionBox = box1;
box1 = box2;
box2 = tmpBox;
vectorToBox1.x = -vectorToBox1.x;
vectorToBox1.y = -vectorToBox1.y;
vectorToBox1.z = -vectorToBox1.z;
faceAxisIdx -= 3;
swapNormal = true;
}
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
var colAxis:Vector3 = contact.normal;
transform1.getAxis(faceAxisIdx, colAxis);
var negativeFace:Boolean = colAxis.x*vectorToBox1.x + colAxis.y*vectorToBox1.y + colAxis.z*vectorToBox1.z > 0;
var code:int = 1 << (faceAxisIdx << 1);
if (negativeFace) {
code <<= 1;
}
if ((code & box1.excludedFaces) != 0) return false;
if (!negativeFace) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
var incidentAxisIdx:int = 0;
var incidentAxisDot:Number;
var maxDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
transform2.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;
incidentAxisDot = dot;
incidentAxisIdx = axisIdx;
}
}
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
transform2.getAxis(incidentAxisIdx, axis);
getFaceVertsByAxis(box2.hs, incidentAxisIdx, incidentAxisDot < 0, points1);
// TODO: Вычислить результирующую матрицу, затем преобразовать векторы за один заход
transform2.transformVectorsN(points1, points2, 4);
transform1.transformVectorsInverseN(points2, points1, 4);
var pnum:int = clip(box1.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
var pen:Number;
pcount = 0;
for (var i:int = 0; i < pnum; i++) {
var v:Vector3 = points1[i];
if ((pen = getPointBoxPenetration(box1.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
var cp:ContactPoint = tmpPoints[pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = transform1.a*v.x + transform1.b*v.y + transform1.c*v.z + transform1.d;
cpPos.y = transform1.e*v.x + transform1.f*v.y + transform1.g*v.z + transform1.h;
cpPos.z = transform1.i*v.x + transform1.j*v.y + transform1.k*v.z + transform1.l;
var r:Vector3 = cp.r1;
r.x = cpPos.x - transform1.d;
r.y = cpPos.y - transform1.h;
r.z = cpPos.z - transform1.l;
r = cp.r2;
r.x = cpPos.x - transform2.d;
r.y = cpPos.y - transform2.h;
r.z = cpPos.z - transform2.l;
cp.penetration = pen;
}
}
if (swapNormal) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
if (pcount > 4) {
reducePoints();
}
for (i = 0; i < pcount; i++) {
cp = contact.points[i];
cp.copyFrom(tmpPoints[i]);
}
contact.pcount = pcount;
return true;
}
/**
*
* @param contactInfo
*/
private function reducePoints():void {
var i:int;
var minIdx:int;
var cp1:ContactPoint;
var cp2:ContactPoint;
while (pcount > 4) {
var minLen:Number = 1e10;
var p1:Vector3 = (tmpPoints[int(pcount - 1)] as ContactPoint).pos;
var p2:Vector3;
for (i = 0; i < pcount; i++) {
p2 = (tmpPoints[i] as ContactPoint).pos;
var dx:Number = p2.x - p1.x;
var dy:Number = p2.y - p1.y;
var dz:Number = p2.z - p1.z;
var len:Number = dx*dx + dy*dy + dz*dz;
if (len < minLen) {
minLen = len;
minIdx = i;
}
p1 = p2;
}
var ii:int = minIdx == 0 ? (pcount - 1) : (minIdx - 1);
cp1 = tmpPoints[ii];
cp2 = tmpPoints[minIdx];
p1 = cp1.pos;
p2 = cp2.pos;
p2.x = 0.5*(p1.x + p2.x);
p2.y = 0.5*(p1.y + p2.y);
p2.z = 0.5*(p1.z + p2.z);
cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
if (minIdx > 0) {
for (i = minIdx; i < pcount; i++) {
tmpPoints[int(i - 1)] = tmpPoints[i];
}
tmpPoints[int(pcount - 1)] = cp1;
}
pcount--;
}
}
/**
*
* @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 clip(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 box1 первый бокс
* @param box2 второй бокс
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
* @param axisIdx1 индекс направляющей оси ребра первого бокса
* @param axisIdx2 индекс направляющей оси ребра второго бокса
* @param contactInfo структура, в которую записывается информация о столкновении
*/
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
transform1.getAxis(axisIdx1, axis10);
transform2.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*vectorToBox1.x + colAxis.y*vectorToBox1.y + colAxis.z*vectorToBox1.z < 0) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Находим среднюю точку на каждом из пересекающихся рёбер
var halfLen1:Number;
var halfLen2:Number;
var tx:Number = box1.hs.x;
var ty:Number = box1.hs.y;
var tz:Number = box1.hs.z;
var x2:Number = box2.hs.x;
var y2:Number = box2.hs.y;
var z2:Number = box2.hs.z;
// x
if (axisIdx1 == 0) {
tx = 0;
halfLen1 = box1.hs.x;
} else {
if (colAxis.x*transform1.a + colAxis.y*transform1.e + colAxis.z*transform1.i > 0) {
tx = -tx;
}
}
if (axisIdx2 == 0) {
x2 = 0;
halfLen2 = box2.hs.x;
} else {
if (colAxis.x*transform2.a + colAxis.y*transform2.e + colAxis.z*transform2.i < 0) {
x2 = -x2;
}
}
// y
if (axisIdx1 == 1) {
ty = 0;
halfLen1 = box1.hs.y;
} else {
if (colAxis.x*transform1.b + colAxis.y*transform1.f + colAxis.z*transform1.j > 0) {
ty = -ty;
}
}
if (axisIdx2 == 1) {
y2 = 0;
halfLen2 = box2.hs.y;
} else {
if (colAxis.x*transform2.b + colAxis.y*transform2.f + colAxis.z*transform2.j < 0) {
y2 = -y2;
}
}
// z
if (axisIdx1 == 2) {
tz = 0;
halfLen1 = box1.hs.z;
} else {
if (colAxis.x*transform1.c + colAxis.y*transform1.g + colAxis.z*transform1.k > 0) {
tz = -tz;
}
}
if (axisIdx2 == 2) {
z2 = 0;
halfLen2 = box2.hs.z;
} else {
if (colAxis.x*transform2.c + colAxis.y*transform2.g + colAxis.z*transform2.k < 0) {
z2 = -z2;
}
}
// Получаем глобальные координаты средних точек рёбер
var x1:Number = transform1.a*tx + transform1.b*ty + transform1.c*tz + transform1.d;
var y1:Number = transform1.e*tx + transform1.f*ty + transform1.g*tz + transform1.h;
var z1:Number = transform1.i*tx + transform1.j*ty + transform1.k*tz + transform1.l;
tx = x2;
ty = y2;
tz = z2;
x2 = transform2.a*tx + transform2.b*ty + transform2.c*tz + transform2.d;
y2 = transform2.e*tx + transform2.f*ty + transform2.g*tz + transform2.h;
z2 = transform2.i*tx + transform2.j*ty + transform2.k*tz + transform2.l;
// Находим точку пересечения рёбер, решая систему уравнений
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
tx = x2 - x1;
ty = y2 - y1;
tz = z2 - z1;
var c1:Number = axis10.x*tx + axis10.y*ty + axis10.z*tz;
var c2:Number = axis20.x*tx + axis20.y*ty + axis20.z*tz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о столкновении
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
var cpPos:Vector3 = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
cp.pos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
cp.pos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
cp.pos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
var r:Vector3 = cp.r1;
r.x = cpPos.x - transform1.d;
r.y = cpPos.y - transform1.h;
r.z = cpPos.z - transform1.l;
r = cp.r2;
r.x = cpPos.x - transform2.d;
r.y = cpPos.y - transform2.h;
r.z = cpPos.z - transform2.l;
cp.penetration = minOverlap;
}
/**
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
*
* @param box1
* @param box2
* @param axis
* @param axisIndex
* @param toBox1
* @param bestAxis
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
*/
private function testMainAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis1
* @param axis2
* @param axisIndex
* @param toBox1
* @return
*/
private function testDerivedAxis(box1:CollisionBox, box2:CollisionBox, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox1: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(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param axis ось
* @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
* @return величина перекрытия боксов вдоль оси
*/
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1:Vector3):Number {
var m:Matrix4 = box1.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
if (d < 0) d = -d;
var projection:Number = d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
if (d < 0) d = -d;
projection += d;
m = box2.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
if (d < 0) d = -d;
projection += d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
if (d < 0) d = -d;
projection += d;
d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
if (d < 0) d = -d;
return projection - d;
}
}
}
import alternativa.physics.math.Vector3;
class CollisionPointTmp {
public var pos:Vector3 = new Vector3();
public var penetration:Number;
public var feature1:int;
public var feature2:int;
}