Как рассчитать столкновение с вращением в трехмерном пространстве?

В моей программе мне нужно рассчитать столкновение между повернутой рамкой и сферой, а также столкновение между 2 повернутыми рамками. Кажется, я не могу найти какую-либо информацию об этом, и попытка выяснить математику в моем собственном воображении поражает меня.

У меня есть столкновение, работающее на 2 ящика и сферу и ящик, но теперь мне нужно учитывать углы. Это мой код до сих пор:

class Box
{
public:
Box();

private:
float m_CenterX, m_CenterY, m_CenterZ, m_Width, m_Height, m_Depth;
float m_XRotation, m_YRotation, m_ZRotation;
};

class Sphere
{
public:
Sphere();

private:
float m_CenterX, m_CenterY, m_CenterZ, radius;
unsigned char m_Colour[3];
};

bool BoxBoxCollision(BoxA, BoxB)
{
//The sides of the Cubes
float leftA, leftB;
float rightA, rightB;
float topA, topB;
float bottomA, bottomB;
float nearA, nearB;
float farA, farB;

//center pivot is at the center of the object
leftA = A.GetCenterX() - A.GetWidth();
rightA = A.GetCenterX() + A.GetWidth();
topA = A.GetCenterY() - A.GetHeight();
bottomA = A.GetCenterY() + A.GetHeight();
farA = A.GetCenterZ() - A.GetDepth();
nearA = A.GetCenterZ() + A.GetDepth();

leftB = B.GetCenterX() - B.GetWidth();
rightB = B.GetCenterX() + B.GetWidth();
topB = B.GetCenterY() - B.GetHeight();
bottomB = B.GetCenterY() + B.GetHeight();
farB = B.GetCenterZ() - B.GetDepth();
nearB = B.GetCenterZ() + B.GetDepth();

//If any of the sides from A are outside of B
if( bottomA <= topB ) { return false; }
if( topA >= bottomB ) { return false; }
if( rightA <= leftB ) { return false; }
if( leftA >= rightB ) { return false; }
if( nearA <= farB ) { return false; }
if( farA >= nearB ) { return false; }

//If none of the sides from A are outside B
return true;
}

bool SphereBoxCollision( Sphere& sphere, Box& box)
{
float sphereXDistance = abs(sphere.getCenterX() - box.GetCenterX());
float sphereYDistance = abs(sphere.getCenterY() - box.GetCenterY());
float sphereZDistance = abs(sphere.getCenterZ() - box.GetCenterZ());

if (sphereXDistance >= (box.GetWidth() + sphere.getRadius())) { return false; }
if (sphereYDistance >= (box.GetHeight() + sphere.getRadius())) { return false; }
if (sphereZDistance >= (box.GetDepth() + sphere.getRadius())) { return false; }

if (sphereXDistance < (box.GetWidth())) { return true; }
if (sphereYDistance < (box.GetHeight())) { return true; }
if (sphereZDistance < (box.GetDepth())) { return true; }

float cornerDistance_sq = ((sphereXDistance - box.GetWidth()) * (sphereXDistance - box.GetWidth())) +
((sphereYDistance - box.GetHeight()) * (sphereYDistance - box.GetHeight()) +
((sphereYDistance - box.GetDepth()) * (sphereYDistance - box.GetDepth())));

return (cornerDistance_sq < (sphere.getRadius()*sphere.getRadius()));
}

Как мне учитывать вращение? Любые предложения будут великолепны.

2

Решение

Прежде всего, ваши объекты — это прямоугольники, а не прямоугольники. Термин прямоугольник строго зарезервирован для 2D фигуры.

Когда вы имеете дело с вращениями, вы должны рассматривать их как особую форму аффинное преобразование. Аффинное преобразование может быть вращением, переводом, операцией масштабирования, операцией сдвига или любой их комбинацией, и оно может быть представлено простой матрицей 4×4, которая умножается на векторы, которые дают вершины ваших блоков. То есть, вы можете описать любую повернутую, масштабированную, сдвинутую коробку как блок блока (блок между векторами <0,0,0> до <1,1,1>) к которому применено аффинное преобразование.

Матрица большинства аффинных преобразований (кроме тех, которые масштабируются с нулевым коэффициентом) может быть инвертирована, так что вы можете преобразовать любую точку в систему координат блока, а затем сравнить ее с <0,0,0> и <1,1,1> проверить, находится ли он внутри блока, и преобразовать любую точку в координатах блока обратно в вашу мировую систему координат (например, вы можете найти центр вашего блока, преобразовав вектор <0,5, 0,5, 0,5>). Поскольку любая прямая линия остается прямой, когда к ней применяется аффинное преобразование, все, что вам когда-либо нужно преобразовать, это вершины ваших блоков.

Теперь вы можете просто взять вершины одной коробки (<0,0,0> <0,0,1>, …), преобразуйте их в свою мировую систему координат, затем преобразуйте их обратно в систему координат другого блока. После этого вопрос о том, перекрываются ли два блока, становится вопросом, перекрывает ли блок, описанный преобразованными восемью вершинами, блок блока. Теперь вы можете легко решить, есть ли вершина над базовой плоскостью блока юнитов (y > 0), ниже верхней плоскости (y < 1), и так далее. К сожалению, для пересечения коробки / коробки существует множество случаев, которые гораздо проще пересекать сферы, лучи, плоскости и т. Д., Чем такие сложные объекты, как коробки. Тем не менее, наличие одной коробки, прибитой к блоку юнита, должно сильно помочь.


Примечание:

Для вращений в 3D стоит знать, как использовать кватернионы для этого. Эйлеровы углы и подобные системы все имеют проблему замок карданного подвеса, кватернионы не имеют этого ограничения.

По сути, каждая единица кватерниона описывает вращение вокруг одной свободной оси. Когда вы умножаете два кватерниона на единицу, вы получаете третий, который дает вам вращение, возникающее в результате применения двух кватернионов один за другим. И, поскольку вычисление мультипликативного обратного кватерниона тривиально, вы также можете разделить один кватернион на другой, чтобы ответить на вопрос, какое вращение одной оси вам нужно применить, чтобы перейти из одного состояния вращения в другое. Эту последнюю часть просто невозможно сделать с точки зрения углов Эйлера. Кватернионы — действительно одна из самых сладких частей математики.

Я просто не могу охватить все детали в этом ответе, тема довольно широкая и интересная. Вот почему я связал четыре статьи Википедии. Прочитайте их, если вам нужна дополнительная информация.

1

Другие решения

Для столкновения Box-Box преобразуйте координаты таким образом, чтобы первый прямоугольник был отцентрирован в начале координат и выровнен по оси. Затем проверить, сталкивается ли второе поле с ним, легче, даже если это не совсем тривиально. В большинстве случаев (физический движок при малых значениях dt * v, где вы можете предположить, что движение непрерывно) достаточно проверить, попадают ли какие-либо из вершин в первый блок.

Для Box-Sphere проще. Как и прежде, преобразуйте координаты таким образом, чтобы прямоугольник центрировался в начале координат и выровнялся по оси. Теперь вам нужно только проверить, что расстояние между центром бокса и каждой из канонических плоскостей (генерируемых осями) меньше радиуса сферы плюс половина пролета бокса в нормальном направлении.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector