Hi,
I've been working on a correct implementation of a OBB and AABB in worldspace for my dynamic mesh instances or renderables.This gives me the flexibility to cull using either a simple sphere check, OBB or AABB worldspace. The AABB I also use for basic collision detection.
When doing all the math, there's something I can't figure out (please not that the results are all fine, just like to understand the math/logics). Here's the code as background:
void OBB::Update(const D3DXMATRIX &pWorldMat, const AABB &pAABB)
{
D3DXVec3TransformCoord(&Center, &pAABB.Center(), &pWorldMat);
D3DXVECTOR3 size = pAABB.Extents();
D3DXVECTOR3 mAx, mAy, mAz;
mAx = D3DXVECTOR3(pWorldMat._11, pWorldMat._12, pWorldMat._13);
mAy = D3DXVECTOR3(pWorldMat._21, pWorldMat._22, pWorldMat._23);
mAz = D3DXVECTOR3(pWorldMat._31, pWorldMat._32, pWorldMat._33);
XHalfExtent = mAx * size.x / 2.0f;
YHalfExtent = mAy * size.y / 2.0f;
ZHalfExtent = mAz * size.z / 2.0f;
}
void AABBWORLD::Update(const OBB &pOBB)
{
/* 1. calculate the 8 corners based on the OBB half extents
E F
/-------------| A = -X -Y -Z
/ | / | B = +X -Y -Z
/ | / | C = +X +Y -Z
A /------------|B | D = -X +Y -Z
| | | |
| / - - - - | - | G E = -X -Y +Z
| / H | / F = +X -Y +Z
|/ | / G = +X +Y +Z
|------------|/ H = -X +Y +Z
D C
*/
D3DXVECTOR3 OBBcorners[8];
OBBcorners[0] = -pOBB.XHalfExtent -pOBB.YHalfExtent -pOBB.ZHalfExtent;
OBBcorners[1] = +pOBB.XHalfExtent -pOBB.YHalfExtent -pOBB.ZHalfExtent;
OBBcorners[2] = +pOBB.XHalfExtent +pOBB.YHalfExtent -pOBB.ZHalfExtent;
OBBcorners[3] = -pOBB.XHalfExtent +pOBB.YHalfExtent -pOBB.ZHalfExtent;
OBBcorners[4] = -pOBB.XHalfExtent -pOBB.YHalfExtent +pOBB.ZHalfExtent;
OBBcorners[5] = +pOBB.XHalfExtent -pOBB.YHalfExtent +pOBB.ZHalfExtent;
OBBcorners[6] = +pOBB.XHalfExtent +pOBB.YHalfExtent +pOBB.ZHalfExtent;
OBBcorners[7] = -pOBB.XHalfExtent +pOBB.YHalfExtent +pOBB.ZHalfExtent;
// 2. Find the min and max X Y Z of the corners to get AABB in worldspace
_Min = OBBcorners[0];
_Max = _Min;
for(int j=1;j<8;++j)
{
_Min = D3DXVECTOR3((std::min)(OBBcorners[j].x, _Min.x),
(std::min)(OBBcorners[j].y, _Min.y),
(std::min)(OBBcorners[j].z, _Min.z));
_Max = D3DXVECTOR3((std::max)(OBBcorners[j].x, _Max.x),
(std::max)(OBBcorners[j].y, _Max.y),
(std::max)(OBBcorners[j].z, _Max.z));
}
// 3. Transfom Min/Max to worldspace by adding world center to the _Min and _Max
Center = pOBB.Center;
_Min += Center;
_Max += Center;
}
// CULLING FUNCTIONS
int CD3dcam::OBBInFrustum(const OBB &pOBB) const
{
int _result = 99;
float d, s;
for(int i=0;i<6;++i)
{
d = D3DXPlaneDotCoord(&mFrustumPlane[i], &pOBB.Center);
s = fabs (D3DXVec3Dot(&D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c), &pOBB.XHalfExtent)) +
fabs (D3DXVec3Dot(&D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c), &pOBB.YHalfExtent)) +
fabs (D3DXVec3Dot(&D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c), &pOBB.ZHalfExtent));
if(d < -s) return OUTSIDE;
if(d+ -s < 0) _result = INTERSECT;
}
if(_result == INTERSECT) return INTERSECT;
else return INSIDE;
}
int CD3dcam::AABBInFrustum(const Crealysm_dxmath::AABBWORLD &pAABBworld) const
{
D3DXVECTOR3 _pvtx, _nvtx;
bool intersect = false;
float dist;
D3DXVECTOR3 center = pAABBworld.Center;
D3DXVECTOR3 extents = pAABBworld.Extents();
for(int i=0;i<6;++i)
{
// find the nearest and farthest point along normal direction
_pvtx.x = center.x + (extents.x / 2.0f) * mFrustumPlaneSign[i].x;
_pvtx.y = center.y + (extents.y / 2.0f) * mFrustumPlaneSign[i].y;
_pvtx.z = center.z + (extents.z / 2.0f) * mFrustumPlaneSign[i].z;
_nvtx.x = center.x - (extents.x / 2.0f) * mFrustumPlaneSign[i].x;
_nvtx.y = center.y - (extents.y / 2.0f) * mFrustumPlaneSign[i].y;
_nvtx.z = center.z - (extents.z / 2.0f) * mFrustumPlaneSign[i].z;
// check positive vertex; further along the normal's direction
dist = D3DXPlaneDotCoord(&mFrustumPlane[i], &_pvtx);
if(dist < 0) return OUTSIDE; // wrong side of plane, completely outside
// check negative vertex; less far along the normal's direction
dist = D3DXPlaneDotCoord(&mFrustumPlane[i], &_nvtx);
if(dist < 0) intersect = true;
}
if(intersect) return INTERSECT;
return INSIDE;
}
Details:
- the OBB extents are transformed by only the 1st, 2nd and 3rd row of the world matrix. My understanding is that the extents are therefor rotated and scaled, but NOT transformed to worlspace (because that would be in the 4th row of my world matrix)
- my OBB culling function compares planes in worldspace and the OBB extents
- the AABB in worldspace is calculated from the OBB, using the extents so in local space (oriented).
Therefor I have to add the center to 'translate' the AABB to worldspace. This gives me the expected results and confirms to me the that the OBB extents are not in worldspace.
So my question is, why does the OBB culling work with this code, without transforming the OBB extents to worldspace? (using the center).
Since the frustum planes I believe are also in worldspace.
Any input is appreciated.