0
\$\begingroup\$

I am trying to make bounding boxes for my models. My code works fine if the model is imported as a COLLADA (.dae) file. When I try to import a model that has .fbx extension, my bounding box messes up. Here's a screenshot (purple cylinder is imported as .dae and the red jet is imported as .fbx):

enter image description here

As you can see the box is perfect for the cylinder but for the jet it's massive. It extends beyond the playing field. I don't understand why this is happening. Here's my code for generating a bounding box:

class MeshModel
    {
        public List<BoundingBox> boundingBoxes;

        public MeshModel(Model model)
        {
            // Set up model data
            boundingBoxes = new List<BoundingBox>();

            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in model.Meshes)
            {
                Matrix meshTransform = transforms[mesh.ParentBone.Index];
                boundingBoxes.Add(BuildBoundingBox(mesh, meshTransform));
            }
        }

        private BoundingBox BuildBoundingBox(ModelMesh mesh, Matrix meshTransform)
        {
            // Create initial variables to hold min and max xyz values for the mesh
            Vector3 meshMax = new Vector3(float.MinValue);
            Vector3 meshMin = new Vector3(float.MaxValue);

            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                // The stride is how big, in bytes, one vertex is in the vertex buffer
                // We have to use this as we do not know the make up of the vertex
                int stride = part.VertexBuffer.VertexDeclaration.VertexStride;

                VertexPositionTexture[] vertexData = new VertexPositionTexture[part.NumVertices];
                part.VertexBuffer.GetData(part.VertexOffset * stride, vertexData, 0, part.NumVertices, stride);

                // Find minimum and maximum xyz values for this mesh part
                Vector3 vertPosition = new Vector3();

                for (int i = 0; i < vertexData.Length; i++)
                {
                    vertPosition = vertexData[i].Position;

                    // update our values from this vertex
                    meshMin = Vector3.Min(meshMin, vertPosition);
                    meshMax = Vector3.Max(meshMax, vertPosition);
                }
            }

            // transform by mesh bone matrix
            meshMin = Vector3.Transform(meshMin, meshTransform);
            meshMax = Vector3.Transform(meshMax, meshTransform);

            // Create the bounding box
            BoundingBox box = new BoundingBox(meshMin, meshMax);
            return box;
        }

        public void Draw(BasicEffect boxEffect, Matrix View, Matrix Projection, Matrix World, GraphicsDevice device)
        {
            // Initialize an array of indices for the box. 12 lines require 24 indices
            short[] bBoxIndices = {
                0, 1, 1, 2, 2, 3, 3, 0, // Front edges
                4, 5, 5, 6, 6, 7, 7, 4, // Back edges
                0, 4, 1, 5, 2, 6, 3, 7 // Side edges connecting front and back
            };

            // Use inside a drawing loop
            foreach (BoundingBox box in boundingBoxes)
            {
                Vector3[] corners = box.GetCorners();
                VertexPositionColor[] primitiveList = new VertexPositionColor[corners.Length];

                // Assign the 8 box vertices
                for (int i = 0; i < corners.Length; i++)
                {
                    primitiveList[i] = new VertexPositionColor(corners[i], Color.White);
                }

                boxEffect.World = World;
                boxEffect.View = View;
                boxEffect.Projection = Projection;
                boxEffect.TextureEnabled = false;

                // Draw the box with a LineList
                foreach (EffectPass pass in boxEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    device.DrawUserIndexedPrimitives(PrimitiveType.LineList, primitiveList, 0, 8, bBoxIndices, 0, 12);
                }
            }
        }
    }

I draw my models like this:

public void Draw(GraphicsDevice graphicsDevice)
{
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.View = camera.View;
            effect.World = worldMatrix;
            effect.Projection = camera.Projection;
            effect.EnableDefaultLighting();

            mesh.Draw();

            // Debug: Draw bounding boxes
            modelBox.Draw(effect, camera.View, camera.Projection, effect.World, graphicsDevice);
        }
    }
}

Any help would be appreciated!

\$\endgroup\$
1
\$\begingroup\$

When you draw the mesh you only supply your worldMatrix, it looks OK because the plane is most likely one mesh or all parts have similar transforms.

First get the Matrix array with all transforms from your model:

Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);

Then in your inner loop, when setting the World matrix, you also multiply with the mesh transform. The mesh local transform (rotation, scale of the current part):

effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;
\$\endgroup\$
  • \$\begingroup\$ Hi! Thanks for the reply. I tried it your way and it works fine for .dae models. However, for .fbx, I can't scale the model anymore. The model comes out as being very huge: i.imgur.com/ACcLKqF.png. You can see the blue floor going through it. To scale it I was doing effect.World = worldMatrix * Matrix.CreateScale(scale);, which gave me the picture in the OP. When I do effect.World = transforms[mesh.ParentBone.Index] * worldMatrix * Matrix.CreateScale(scale);, I get the picture I posted. How can I scale it? I can't tell if the bounding boxes are working for it. \$\endgroup\$ – syy Aug 25 '16 at 19:49
  • \$\begingroup\$ I think you should do: effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateScale(scale) * worldMatrix ; \$\endgroup\$ – Stefan Agartsson Aug 25 '16 at 21:41
  • \$\begingroup\$ That helped with the scale. The model becomes smaller now. However, I don't see any bounding box around it. If I do a scale lower than Matrix.CreateScale(0.03f) (e.g. 0.01f), then the bounding box appears like this: i.imgur.com/awYk3qj.png. If it would be easier, I can give you the entire project (really small ~ 5 classes). I understand if you don't want it. In any case, I really appreciate the help! \$\endgroup\$ – syy Aug 25 '16 at 23:06
0
\$\begingroup\$

I see you created a Matrix[] called transforms for use while building your boxes (BuildBoundinBox(ModelMeshesh, Matrix[])). That Matrix[] modifies the position, rotation, scale of the boxes. This causes the size of the boxes to be what they are when drawn.

You need to create a similar Matrix[] to for the model itself that get applied to the effect.World when drawing the model so it gets the same changes that the boxes have.

My guess is that .dae bakes the transforms into the output of the model application exporter. .fbx doesn't. It's actually more flexible that way.

\$\endgroup\$
  • \$\begingroup\$ I don't fully understand what you mean. Should I create a list of the transforms and multiply it by effect.World when drawing? Sorry, I'm still learning the terminology and how everything works. \$\endgroup\$ – syy Aug 16 '16 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.