Software skinning for a precise AABB

Started by
41 comments, last by Alundra 8 years, 4 months ago

I've implemented frustum culling and it works great for static meshes, but skeletal animated meshes might go outside of the pre-calculated aabb (or sphere), and still get culled.

So the best solution I could think of is to go through all the animation sets when the model is loaded, and calculate the maximum aabb that can possibly be.

I already do skinning in the glsl vertex shader, but software skinning sounds harder to implement.

The implementation stages I have in my head are these:

When loading a model:

1. Load all meshes. (don't dump vertices and vertex weights that are stored on the cpu yet)

2. Load all animation sets.

3. Go through all animation sets, calculate bone transforms for every key frame, and transform all the meshes' vertices.

For each key frame, calculate the entire model's AABB, and merge it with the previous AABB.

This should take my much time to implement, so I wanted to ask here if it sounds okay and if there's anything I'm missing.

Advertisement
Unity solves this problem by letting the user manually set the bounding box for the skinned mesh. Perhaps before trying to make an automated solution, you could simply do that.
My current game project Platform RPG

That sounds like a lot of extra effort for a solution. Why check on a per bone BB. Ask yourself if that much accuracy is necessary? Perhaps a single OOBB/AABB will suffice to cover the whole animated object? A lot less cycles are wasted when checking only a single BB instead of an unknown n amount, sacrificing resolution for performance.

Have you considered an OBB for each bone which after you transform you create an AABB from? Both for each bone and for the whole object? I think thats right.

-potential energy is easily made kinetic-

Have you considered an OBB for each bone which after you transform you create an AABB from? Both for each bone and for the whole object? I think thats right.

This is the common way used in all big animation system, it's computed at export time.

The second option if you know the animation list is to compute the AABB of each animation, find the bigger AABB for all animations.

The last option as said before is to set manually a bounding box.

On the second and last option you can also compute the AABB big enough to handle all rotations to avoid to transform it with the rotation of the object.

The AABB of the bind pose should be conservative enough to use for culling, unless the object being skinned have subobjects ( ex. tentacles ), that my extend beyond the bind pose when animated. The approach you propose would work, but you could take it a step further and probably do that at export time ( if you are using a custom format. )

For the OBB, the export time option works good, for other option since you can retarget any animation to one skeleton, you can't know exactly what animation list will be played.

All the listed options are incredibly slow for having many instances in real time running at the same time (except the ones that suggest baking the AABB data).

The best solution that is good enough for 99% of cases is to save the AABBs at different frame intervals (granted, you need to do software skinning first) store them (e.g. in the mesh file), and then linearly interpolate between those AABBs.

Btw sw skinning takes no sh@t time to implement, here:

//Parameters
const Matrix4x4 * __restrict poseMatrix;
uint8_t const  * __restrict vertexData;
size_t posStride;
size_t blendIndexStride;
size_t blendWeightStride;
size_t numVertices;
size_t bytesPerVertex;
int numWeightsPerVertex;

Vector3 max( -std::numeric_limits<float>::max() );
Vector3 min(  std::numeric_limits<float>::min() );

for( size_t i=0; i<numVertices; ++i )
{
	const Vector3 __restrict  *vPos = reinterpret_cast<const Vector3 __restrict  *>( vertexData + posStride );
	const uint8_t __restrict  *blendIndex = reinterpret_cast<const uint8_t __restrict  *>( vertexData + blendIndexStride );
	const float __restrict  *blendWeight = reinterpret_cast<const float __restrict  *>( vertexData + blendWeightStride );
	
	for( int j=0; j<numWeightsPerVertex; ++j )
	{
		const Vector3 translatedPos = poseMatrix[blendIndex[j]] * vPos;
		
		max.x = std::max( max.x, translatedPos.x );
		max.y = std::max( max.y, translatedPos.y );
		max.z = std::max( max.z, translatedPos.z );
		min.x = std::min( min.x, translatedPos.x );
		min.y = std::min( min.y, translatedPos.y );
		min.z = std::min( min.z, translatedPos.z );
	}
	vertexData += bytesPerVertex;
}
There. I wrote it in under 5 minutes. Now you have no excuse.
poseMatrix is the matrix you would normally pass to the vertex shader but without the world space component (so that the operation is done in local space).


The best solution that is good enough for 99% of cases is to save the AABBs at different frame intervals (granted, you need to do software skinning first) store them (e.g. in the mesh file), and then linearly interpolate between those AABBs.

I wouldn't do that, just go for a static AABB as mentioned before.

What you can do is simply rotate your model in bind pose in some loop or so, and find the maximum AABB, this works pretty well.

Storing AABB's and interpolating them is really overkill, and you need to pass it around your blend trees and handle all the blending and logic in there as well then to get the right AABB. It isn't really worth it I think. Then you could also just build the bounds from the transformed obb's at the end. I doubt that is much slower than this with large blend trees involved.

I agree with Buckshag. Interpolation seems a bit overkill.

@Matias Thanks for the code snippet. Is poseMatrix the Bones[] array I pass to the vertex shader per frame?

Btw, what's up with all those __restricts and reinterpret casts? :P

This topic is closed to new replies.

Advertisement