using UnityEngine;
using System.Collections;
public class OBB : MonoBehaviour {
// исходные параметры
readonly public Vector3 pos;
readonly public Quaternion rot;
readonly public Vector3 size;
// производные параметры
readonly public Vector3[] bounds = new Vector3[8]; // углы
readonly public float len; // длина диагонали
// ПЕРЕЧИСЛЕНИЯ
public enum obb_axis { x, y, z };
// грани по осям (': uint' - бестолку, Unity пофигу на указаный класс перечисления)
enum face {
PosX, NegX, PosY, NegY, PosZ, NegZ
};
// грани по сторонам редактора
enum face_dir {
right, left, top, bottom, forward, back
};
protected OBB(Transform ts, Vector3 sizeValue) {
pos = ts.position; rot = ts.rotation;
bounds = EvaluateBoundsOBB(ts, sizeValue);
size = sizeValue;
len = sizeValue.magnitude;
}
// вычислить координаты углов параллелепипеда
/*
1 - Transform объекта,
2 - Vector3 с размерами по направляющим осям x, y, z
*/
public static Vector3[] EvaluateBoundsOBB(Transform trans, Vector3 sizeValue) {
Vector3[] b = new Vector3[8];
// вектора направл. осей осей
Vector3 x = trans.right, y = trans.up, z = trans.forward;
// пихаем в них половины векторов-размеров
x *= 0.5f*sizeValue.x;
y *= 0.5f*sizeValue.y;
z *= 0.5f*sizeValue.z;
// верхняя грань
b[0] = -x + y + z;
b[1] = -x + y - z;
b[2] = x + y - z;
b[3] = x + y + z;
// нижняя грань
b[4] = -x - y + z;
b[5] = -x - y - z;
b[6] = x - y - z;
b[7] = x - y + z;
return b;
}
static public bool TestIntersectionAxis(OBB obb1, OBB obb2, obb_axis name_axis) {
// [F,A] номера углов A, принадлежащих F грани
uint[,] angles = new uint[6,4] { // const
{ 2,3,6,7 }, { 0,1,4,5 },
{ 0,1,2,3 }, { 4,5,6,7 },
{ 0,3,4,7 }, { 0,1,4,5 }
};
int axis = (int)name_axis; // переводим имя оси в номер
// предпологаем что первый OBB левее по оси, чем второй
if (obb1.pos[axis] > obb2.pos[axis]) {
// Мы ошиблись. Меняем их местами
OBB obb_temp = obb1;
obb1 = obb2; obb2 = obb_temp;
}
// ВЫЯСНЯЕМ - ЕСТЬ ЛИ ПЕРЕСЕЧЕНИЕ ПО ОСИ name_axis
uint i = 0;
// запоминаем номера граней в зависимости от оси
uint face1 = 0, face2 = 0;
if (axis == 0 /*X*/) { face1 = (uint)face.PosX /*0*/; face2 = (uint)face.NegX /*1*/; };
if (axis == 1 /*Y*/) { face1 = (uint)face.PosY /*2*/; face2 = (uint)face.NegY /*3*/; };
if (axis == 2 /*Z*/) { face1 = (uint)face.PosZ /*4*/; face2 = (uint)face.NegZ /*5*/; };
// ищем самые правые углы для 1-ого OBB и самые левые - для второго OBB
// первые углы
float close_angle1, close_angle2;
close_angle1 = obb1.bounds[angles[face1,i]][axis];
close_angle2 = obb2.bounds[angles[face2,i]][axis];
// остальные 3
for (i = 1; i < 4; i++) {
float new_angle1 = obb1.bounds[angles[face1,i]][axis];
float new_angle2 = obb2.bounds[angles[face2,i]][axis];
if (new_angle1 > close_angle1) close_angle1 = new_angle1;
if (new_angle2 < close_angle2) close_angle2 = new_angle2;
}
// если крайний угол 2-го OBB левее, чем у 1-го, значит есть пересечение
if ((obb1.pos[axis]+close_angle1) > (obb2.pos[axis]+close_angle2)) return true;
return false;
}
static public bool TestIntersection(OBB obb1, OBB obb2) {
// если расстояние между центрами OBB больше, чем сумма их половин диагоналей, то они по любому не могут пересечься
if (Vector3.Distance(obb1.pos, obb2.pos) > (0.5f*obb1.len+0.5f*obb2.len)) return false;
if (TestIntersectionAxis(obb1, obb2, obb_axis.x) && TestIntersectionAxis(obb1, obb2, obb_axis.y) && TestIntersectionAxis(obb1, obb2, obb_axis.z)) return true;
return false;
}
// проверка наложения 2-х зон
static public bool TestIntersection2(OBB obb1, OBB obb2) {
//WalkZone z1 = this[n1]; WalkZone z2 = this[n2];
Matrix3x3 A, B;
A = Convert.QuaternionToMatrix3x3(obb1.rot.x, obb1.rot.y, obb1.rot.z, obb1.rot.w);
B = Convert.QuaternionToTransposeMatrix3x3(obb2.rot.x, obb2.rot.y, obb2.rot.z, obb2.rot.w);
// смещение в мировой системе координат
Vector3 v = obb2.pos - obb1.pos;
// смещение в системе координат первого OBB
Vector3 T = A.TransformVector(v); //A * v;
// матрица поворота A относительно B
Matrix3x3 R = A * B;
float ra, rb, t;
uint i, k;
float[] a = {obb1.size.x, obb1.size.y, obb1.size.z};
float[] b = {obb2.size.x, obb2.size.y, obb2.size.z};
//система координат А
for( i=0 ; i<3 ; i++ )
{
ra = obb1.size[(int)i];
rb = obb2.size[0]*Mathf.Abs(R[i,0]) + obb2.size[1]*Mathf.Abs(R[i,1]) + obb2.size[2]*Mathf.Abs(R[i,2]);
t = Mathf.Abs(T[(int)i]);
//Debug.Log(t - (ra+rb));
if ( t > ra + rb ) return false;
}
//система координат B
for( k=0 ; k<3 ; k++ )
{
ra = a[0]*Mathf.Abs(R[0,k]) + a[1]*Mathf.Abs(R[1,k]) + a[2]*Mathf.Abs(R[2,k]);
rb = b[(int)k];
t = Mathf.Abs( T[0]*R[0,k] + T[1]*R[1,k] + T[2]*R[2,k] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
}
//9 векторных произведений
//L = A0 x B0
ra = a[1]*Mathf.Abs(R[2,0]) + a[2]*Mathf.Abs(R[1,0]);
rb = b[1]*Mathf.Abs(R[0,2]) + b[2]*Mathf.Abs(R[0,1]);
t = Mathf.Abs( T[2]*R[1,0] - T[1]*R[2,0] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A0 x B1
ra = a[1]*Mathf.Abs(R[2,1]) + a[2]*Mathf.Abs(R[1,1]);
rb = b[0]*Mathf.Abs(R[0,2]) + b[2]*Mathf.Abs(R[0,0]);
t = Mathf.Abs( T[2]*R[1,1] - T[1]*R[2,1] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A0 x B2
ra = a[1]*Mathf.Abs(R[2,2]) + a[2]*Mathf.Abs(R[1,2]);
rb = b[0]*Mathf.Abs(R[0,1]) + b[1]*Mathf.Abs(R[0,0]);
t = Mathf.Abs( T[2]*R[1,2] - T[1]*R[2,2] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A1 x B0
ra = a[0]*Mathf.Abs(R[2,0]) + a[2]*Mathf.Abs(R[0,0]);
rb = b[1]*Mathf.Abs(R[1,2]) + b[2]*Mathf.Abs(R[1,1]);
t = Mathf.Abs( T[0]*R[2,0] - T[2]*R[0,0] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A1 x B1
ra = a[0]*Mathf.Abs(R[2,1]) + a[2]*Mathf.Abs(R[0,1]);
rb = b[0]*Mathf.Abs(R[1,2]) + b[2]*Mathf.Abs(R[1,0]);
t = Mathf.Abs( T[0]*R[2,1] - T[2]*R[0,1] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A1 x B2
ra = a[0]*Mathf.Abs(R[2,2]) + a[2]*Mathf.Abs(R[0,2]);
rb = b[0]*Mathf.Abs(R[1,1]) + b[1]*Mathf.Abs(R[1,0]);
t = Mathf.Abs( T[0]*R[2,2] - T[2]*R[0,2] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A2 x B0
ra = a[0]*Mathf.Abs(R[1,0]) + a[1]*Mathf.Abs(R[0,0]);
rb = b[1]*Mathf.Abs(R[2,2]) + b[2]*Mathf.Abs(R[2,1]);
t = Mathf.Abs( T[1]*R[0,0] - T[0]*R[1,0] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A2 x B1
ra = a[0]*Mathf.Abs(R[1,1]) + a[1]*Mathf.Abs(R[0,1]);
rb = b[0] *Mathf.Abs(R[2,2]) + b[2]*Mathf.Abs(R[2,0]);
t = Mathf.Abs( T[1]*R[0,1] - T[0]*R[1,1] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
//L = A2 x B2
ra = a[0]*Mathf.Abs(R[1,2]) + a[1]*Mathf.Abs(R[0,2]);
rb = b[0]*Mathf.Abs(R[2,1]) + b[1]*Mathf.Abs(R[2,0]);
t = Mathf.Abs( T[1]*R[0,2] - T[0]*R[1,2] );
//Debug.Log(t - (ra+rb));
if ( t > (ra + rb) ) return false;
return true;
}
}