Due to my game objects being able to rotate, I went with bounding spheres for the fast-rejection phase. I could have dealt with adjusting an AABB (axis-aligned bounding box[sup]2[/sup]) for rotation or using OBB (oriented bounding box), but decided to go with bounding spheres for the simplicity. XNA already provides a BoundingSphere struct that can be tested for intersection against a ray, so it was the natural choice to use that.
For the more precise phase, I could have gone against a collision mesh and gotten very accurate results from that. But for the sake of playing around a bit, I decided to take a different approach, which I think turned out to be a good exploration of XNA's Ray and BoundingSphere structs. I used an XML file to specify spheres that closely approximate the mesh. In order to allow these spheres to approximate the mesh, they can be scaled and translated along each axis as well as rotated about an arbitrary axis. Each sphere is also attached to a bone so that animation is taken into account.
Turret and tank models
Turret and tank collision spheres
Manipulating spheres this way is just applying what I learned in my college computer graphics course's ray tracer. If you have never written a ray tracer, I highly recommend the exercise. In that program, there were different primitives that we needed to support (spheres, triangles, cubes, cylinders, etc). The primitives were all unit size and centered at the origin. In order to get a non-unit sphere or triangle, a scale operation had to be applied, which adjusted the primitive instance's world matrix. When it came to rendering, the ray was just multiplied by the inverse of the world matrix. It is worth noting that to use just the inverse both the ray's position and direction were done as Vector4 with the w component being 1 and 0 respectively.
In the tank game, for checking the collision spheres against the ray, I tried adjusting an instance of XNA's Ray struct in the same way and once again using XNA's BoundingSphere struct for the intersection test. This did not work. From looking at the documentation of BoundingSphere.Intersects, I didn't see a reason why it wouldn't. So the next step in my debugging was extracting the relevant values with break points and manually doing the math for the hit and miss test cases. The math worked out correctly. Turns out on the Ray's member documentation page, the direction is specified as a unit vector. There is no note that using a non-unit direction would cause the Intersects method to give incorrect results. This is relevant because by scaling the collision spheres the converted ray's direction will not be a unit length (unless the resulting scale is 1). My solution to this was to implement the ray-sphere intersection test myself in my CollisionSphere class. Another solution would have been to convert the ray then normalize the direction. However, that will give an incorrect t value back, which will mess up determining which is the closest intersection to the ray's source and positioning cross-hairs in 3D space. Maybe it's because of my prior experience for this, but I find it bizarre that XNA's Ray struct has this unit length requirement.
[sup]1[/sup] It has already taken substantially longer than I expected, but I am learning not only the framework, but also more about game development. So, I still intend to complete this project.
[sup]2[/sup] While I assume that most people reading this already know that AABB stands for axis-aligned bounding box, I prefer to define the meanings of all abbreviations at least once just in case someone is unfamiliar.