Strange rotation sympton, aabb extents (with video)

Started by
26 comments, last by cozzie 11 years ago
Ok, clear.
I can ignore updating min/max and corner vertices (unless used for other purposes). Initially I'll use them to find center and size.

For the ax, ay and yz I'll pick _m11, _m21, _m31, _m41 for x out the worldmatrix (2nd column for y, 3rd for z). Which includes both translation and rotation, bringing me to worldspace.

Now I fully understand i'll go implement it now and keep you posted.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement

You can cull the OBB directly against the frustum without transforming anything.

No you can't. By definition, the box has an orientation, and so that MUST be taken into account.

The box centre is translated by the world matrix. The box size is the size in model coordinates and does not change. ax, ay and az come straight out of the world matrix. The bounding box vertices are not used in the method I described, nor are min and max.

Your method is actually identical to mine (albeit with the origin in the middle of the box, rather than a corner). The big difference, is that you are employing a load of dot products to perform an inverse transformation on each plane equation / vector. This is exactly why I recommended performing the inverse transformation on the OBB matrix!! To make this as simple to understand as possible.... consider these two methods to inverse trnasform some points:

void inverseTransformPointsA(Matrix parent, std::vector<Vector3>& points)
{
   for(uint32_t i =0; i< children.size(); ++i)
   {
      Vector3 p = points - parent.w;
      float px = dot( parent.x, p.x ) / dot( parent.x, parent.x );
      float py = dot( parent.y, p.y ) / dot( parent.y, parent.y );
      float pz = dot( parent.z, p.z ) / dot( parent.z, parent.z );
      points = Vector3( px, py, pz );
   }
}
void inverseTransformPointsB(Matrix parent, std::vector<Vector3>& points)
{
   Matrix inverse_parent = invert(parent);
   for(uint32_t i =0; i< children.size(); ++i)
   {
      points = inverse_parent.x * points.x;
      points += inverse_parent.y * points.y;
      points += inverse_parent.z * points.z;
      points += inverse_parent.w;
   }
}


Of those two methods, the latter method can be computed with 3 mult + 3 add per vertex (when using SIMD). The first method requires a large number of dot products, which can't be vectorized easily (which should be apparent since it's computing x/y/z separately). It also requires a number of divisions, unless (as you have done) you say that scale is not allowed in your transformation matrix. Whilst there is _mm_dp_ps available in SSE4, it's not exactly quick (a single _mm_dp_ps instruction will take longer to execute than my entire inner loop). The latter method handles scaled objects without a problem.
I was trying to get away from the idea of transforming all the vertices. Transforming the planes into model space is a valid way to go. (Although it would lead to some questions over which parts can be pre-calculated in this case versus the unrotated case.)

As for the most efficient way of vectorising this, I think that's another discussion.

Good to hear there are (at least) 2 approaches. Not sure though if I understand the one where I don't transform the vertices.

Trying to understand and use the other 'version' now first (and maybe later optimize/ beter vectorising).

Almost there but not the expected result unfortunately.

I checked/ debugged and see that the OBBcenter is correctly translated (center of the OBB).

Will dig into it to try to find what's wrong, as always, any advice is welcome smile.png


// checking OBB against frustum

int CD3dcam::OBBInFrustum(BOUNDINGBOX *pBoundingBox, D3DXMATRIX *pWorldMatrix)
{
	float d, s;

	for(int i=0;i<6;++i)
	{
		D3DXVECTOR3 planevector = D3DXVECTOR3(mFrustumPlane.a, mFrustumPlane.b, mFrustumPlane.c);
		
		d = D3DXVec3Dot(&pBoundingBox->OBBcenter, &planevector);
		s = abs	(D3DXVec3Dot(&D3DXVECTOR3(pWorldMatrix->_11, pWorldMatrix->_21, pWorldMatrix->_31), &planevector)) * pBoundingBox->OBBsize.x +
			abs	(D3DXVec3Dot(&D3DXVECTOR3(pWorldMatrix->_12, pWorldMatrix->_22, pWorldMatrix->_32), &planevector)) * pBoundingBox->OBBsize.y +
			abs	(D3DXVec3Dot(&D3DXVECTOR3(pWorldMatrix->_13, pWorldMatrix->_23, pWorldMatrix->_33), &planevector)) * pBoundingBox->OBBsize.z;

		if(d > s) return INTERSECT;		// intersecting, early quit??
		if(d < -s) return OUTSIDE;
	}
	return INSIDE;
}


// creating the AABB initially

void CreateAABB(TVERTEX *pVtxArray, DWORD pStart, DWORD pNr, BOUNDINGBOX *pBox)
{
	// MIN and MAX vertices - modelspace
	pBox->AABBmin.x = pVtxArray[pStart].position.x;
	pBox->AABBmin.y = pVtxArray[pStart].position.y;
	pBox->AABBmin.z = pVtxArray[pStart].position.z;

	pBox->AABBmax.x = pVtxArray[pStart].position.x;
	pBox->AABBmax.y = pVtxArray[pStart].position.y;
	pBox->AABBmax.z = pVtxArray[pStart].position.z;

	for(DWORD vc=pStart;vc<pStart+pNr;++vc)
	{
		if(pVtxArray[vc].position.x < pBox->AABBmin.x) pBox->AABBmin.x = pVtxArray[vc].position.x;
		if(pVtxArray[vc].position.x > pBox->AABBmax.x) pBox->AABBmax.x = pVtxArray[vc].position.x;

		if(pVtxArray[vc].position.y < pBox->AABBmin.y) pBox->AABBmin.y = pVtxArray[vc].position.y;
		if(pVtxArray[vc].position.y > pBox->AABBmax.y) pBox->AABBmax.y = pVtxArray[vc].position.y;

		if(pVtxArray[vc].position.z < pBox->AABBmin.z) pBox->AABBmin.z = pVtxArray[vc].position.z;
		if(pVtxArray[vc].position.z > pBox->AABBmax.z) pBox->AABBmax.z = pVtxArray[vc].position.z;
	}

	pBox->AABBsize.x = pBox->AABBmax.x - pBox->AABBmin.x;
	pBox->AABBsize.y = pBox->AABBmax.y - pBox->AABBmin.y;
	pBox->AABBsize.z = pBox->AABBmax.z - pBox->AABBmin.z;

	pBox->AABBcenter = pBox->AABBmax - (pBox->AABBsize / 2.0f);

	pBox->created = true;
}

// after loading a mesh instance and-or when object is rotated/moved

	D3DXVec3TransformCoord(&mBoundingBox.OBBcenter, &mBoundingBox.AABBcenter, &mMatWorld);	
	mBoundingBox.OBBsize = mBoundingBox.AABBsize; // ONLY UPDATE WITH SCALING

I also tried to use 'size.x / 2.0f' etc, but no effect.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Update;

I didn't normalize the axes from the world matrix, so just did that (before I can calculate the dot product with size).

Unfortunately not the expected result yet, culling happens to late (objects way out of the frustum and still not culled, till 'far away' from the objects).

What I have now:


int CD3dcam::OBBInFrustum(BOUNDINGBOX *pBoundingBox, D3DXMATRIX *pWorldMatrix)
{
	float d, s;

	for(int i=0;i<6;++i)
	{
		D3DXVECTOR3 planevector = D3DXVECTOR3(mFrustumPlane.a, mFrustumPlane.b, mFrustumPlane.c);

		D3DXVECTOR3 ax, ay, az;
		D3DXVec3Normalize(&ax, &D3DXVECTOR3(pWorldMatrix->_11, pWorldMatrix->_21, pWorldMatrix->_31));
		D3DXVec3Normalize(&ay, &D3DXVECTOR3(pWorldMatrix->_12, pWorldMatrix->_22, pWorldMatrix->_32));
		D3DXVec3Normalize(&az, &D3DXVECTOR3(pWorldMatrix->_13, pWorldMatrix->_23, pWorldMatrix->_33));

		d = D3DXVec3Dot(&pBoundingBox->OBBcenter, &planevector);
		s = abs	(D3DXVec3Dot(&ax, &planevector)) * pBoundingBox->OBBsize.x +
			abs	(D3DXVec3Dot(&ay, &planevector)) * pBoundingBox->OBBsize.y +
			abs	(D3DXVec3Dot(&az, &planevector)) * pBoundingBox->OBBsize.z;

		if(d > s) return INTERSECT;		// intersecting, early quit??
		if(d < -s) return OUTSIDE;
	}
	return INSIDE;
}

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Dot of the centre with the plane should be D3DXPlaneDotCoord, I'm sure you were using that before. And don't return INTERSECT before the end because another plane could still reject it.

Thanks, got it 'improved' I think.

Not sure though what's going wrong.


I've done some debugging, and when an object is expected to be culled (outside all 6 frustum planes), I get the following values for d and s:

plane 1: d = 25, s = 10

plane 2: d = -5.4, s = 11.6

plane 3: d = 5.44, s = 8.3

plane 4: d = 7.5, s = 12.5

plane 5: d = 16.5, s = 12.5

plane 6: d = 133, s = 12.5

For plane 1, 5 and 6 d > s, so it returns inside, where I expected all 6 to be d < -s.


int CD3dcam::OBBInFrustum(BOUNDINGBOX *pBoundingBox, D3DXMATRIX *pWorldMatrix)
{
	float d, s;

	for(int i=0;i<6;++i)
	{
		D3DXVECTOR3 planevector = D3DXVECTOR3(mFrustumPlane.a, mFrustumPlane.b, mFrustumPlane.c);

		D3DXVECTOR3 ax = D3DXVECTOR3(pWorldMatrix->_11, pWorldMatrix->_21, pWorldMatrix->_31);
		D3DXVECTOR3 ay = D3DXVECTOR3(pWorldMatrix->_12, pWorldMatrix->_22, pWorldMatrix->_32);
		D3DXVECTOR3 az = D3DXVECTOR3(pWorldMatrix->_13, pWorldMatrix->_23, pWorldMatrix->_33);
		D3DXVec3Normalize(&ax, &ax);
		D3DXVec3Normalize(&ay, &ay);
		D3DXVec3Normalize(&az, &az);

		d = D3DXPlaneDotCoord(&mFrustumPlane, &pBoundingBox->OBBcenter);
		
		s = abs	(D3DXVec3Dot(&ax, &planevector)) * pBoundingBox->OBBsize.x +
			abs	(D3DXVec3Dot(&ay, &planevector)) * pBoundingBox->OBBsize.y +
			abs	(D3DXVec3Dot(&az, &planevector)) * pBoundingBox->OBBsize.z;

		if(d > s) return INSIDE;		
		if(d < -s) return OUTSIDE;
	}
	return INTERSECT;
}

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Should be impossible to be outside all 6 planes. Just pick one and debug that. Centre dot plane should be zero if the box centre is exactly on the plane. Note that this is very similar to sphere culling, with s replacing the radius.

Thanks, I'll debug some more to find out.

What I also was thinking about, is find the relation in the 'd > s' and 'd < -s' check, could I explain this as checking the nearest and farthest extents of the OBB?

(basically checking a point like you said, with sphere checking)

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Hi Ewclay,

Think I found something while debugging, when I use size /2 in the equation (so half size basically), it's now working correct for one plane.

I think I'm rejecting/ culling to early, after only one plane is processed.

If I take out if d > s RETURN INSIDE and only reject when d < -s I'm all good on the result (but possibly checking to much).

Any pointers I'd really appreciate.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement