In the example I want to use to demonstrate my problem, I have a single model of a turbine and a small grid as shown in the images below. The bounding box of the model is displayed as a red box. I try to check which nodes in my green grid lie above the turbine's bounding box. If a node lies above the bounding box, I move it down a bit, so I can clearly see which nodes are over the bounding box and which are not.
As you can see in the first image, it works perfectly fine for my non-rotated model. However, if I rotate the model by -90° all hit tests fail. I do not understand why this happens! I have the same problem when doing my hit tests for 'Picking'.
Here is my small function I use to check whether a node is above any bounding box or not. For this task, I create a ray for each of my nodes which points to the bottom. Then, I check this ray against intersection with all positions of all my models. (If I had multiple turbines, I'd only have on turbine model, but multiple positions of it. This makes it faster to render on the GPU.)
foreach (var node in level.VisualGraph.Nodes)
{
SharpDX.Vector3 upperVector = new SharpDX.Vector3(node.Position.X, 1.0f, node.Position.Z);
SharpDX.Ray ray = new SharpDX.Ray(upperVector, new SharpDX.Vector3(0.0f, -1.0f, 0.0f));
foreach (var model in level.Models)
{
foreach (var position in model.Positions)
{
if (position.BoundingBox.GeometricBoundingBox.Intersects(ref ray))
{
node.Position = new SharpDX.Vector3(node.Position.X, 0.6f, node.Position.Z);
break;
}
}
}
}
As noted before, each model can occur several times in the scene, so I store each model only once and calculate the transformation for each of its positions. So, there is one bounding box for each position of a model.
The 'GeometricBoundingBox' I am using for the hit test is of type SharpDX.BoundingBox. When I add a new position to my model, I create a new bounding box by checking all vertices of the model for its minimum and maximum.
bool isFirst = true;
foreach (var vertex in vertices)
{
if (isFirst)
{
_min = new SharpDX.Vector3(vertex.GetPosition().X, vertex.GetPosition().Y, vertex.GetPosition().Z);
_max = new SharpDX.Vector3(vertex.GetPosition().X, vertex.GetPosition().Y, vertex.GetPosition().Z);
isFirst = false;
}
else
{
_min.X = Math.Min(_min.X, vertex.GetPosition().X);
_min.Y = Math.Min(_min.Y, vertex.GetPosition().Y);
_min.Z = Math.Min(_min.Z, vertex.GetPosition().Z);
_max.X = Math.Max(_max.X, vertex.GetPosition().X);
_max.Y = Math.Max(_max.Y, vertex.GetPosition().Y);
_max.Z = Math.Max(_max.Z, vertex.GetPosition().Z);
}
}
When the bounding box is created for the first time or whenever the world matrix of the parent position changes, I calculate my 'GeometricBoundingBox ' as follows:
SharpDX.Vector4 testMin = Helper.Math.Multiply(new SharpDX.Vector4(_min, 1.0f), worldMatrix);
SharpDX.Vector4 testMax = Helper.Math.Multiply(new SharpDX.Vector4(_max, 1.0f), worldMatrix);
GeometricBoundingBox = new SharpDX.BoundingBox(new SharpDX.Vector3(testMin.X, testMin.Y, testMin.Z), new SharpDX.Vector3(testMax.X, testMax.Y, testMax.Z));
The world matrix which is used for transforming the model for a position is also used for calculating the bounding box as shown above. I calculate it like this:
public void UpdateWorldMatrix()
{
SharpDX.Matrix rotationMatrix = SharpDX.Matrix.RotationYawPitchRoll(RotationY, RotationX, RotationZ);
// We need to calculate the transformation matrix once and apply it for the initial offset before we can calculate the final matrix.
SharpDX.Matrix initialMatrix = SharpDX.Matrix.Scaling(Scaling * _parentModel.InitialScaling) * rotationMatrix;
SharpDX.Vector3 initialTranslation = Helper.Converters.ToVector(Helper.Math.Multiply2(_parentModel.InitialTranslation, initialMatrix));
// Calculate world matrix and update bounding box.
WorldMatrix = SharpDX.Matrix.Scaling(Scaling * _parentModel.InitialScaling) * rotationMatrix * SharpDX.Matrix.Translation(Position + Translation + initialTranslation);
if (ParentStaticPosition != null)
WorldMatrix = SharpDX.Matrix.Multiply(WorldMatrix, ParentStaticPosition.WorldMatrix);
BoundingBox.ReBuild(WorldMatrix);
}
The bounding box perfectly fits around my turbine model in both models. While the model is transformed on GPU for each position, the bounding box is transformed by the code shown above. Since the model and the bounding fit, I believe that calculating them works.
But I do not understand why my hit tests work for the non-rotated object but I doesn't work for the rotated one.
Do you have any ideas?
One thing you can do which i do for ray casts is to convert the ray to model space rather than convert the bounding box to your orientation. This is done by taking the inverse matrix of model orientation Matrix and multiplying your start and end points of your vector by the inverse. Then creating the ray in model space and casting it against the model space aabb.
If you orientate your aabb by the matrix this won't work. You need to recalculate the new aabb by multiplying every vert in the model against the orientation matrix and then recalculate the aabb. I believe that's what you are doing but i believe the bounding box you are transforming may not have correct max min extents.