Hiding Unseen Objects

Jan 24, 2009 at 6:59 PM
Edited Jan 24, 2009 at 11:00 PM
Whenever you add a lot of objects (especially high poly models) to your world, your FPS begins to drop. So, instead of rendering everything in the world, I decided to only render and draw what is in my FOV. To do this, I created a quick method to check if objects are within my FOV. I also have to change my camera's FOV to 50 instead of the default 45. Otherwise, some objects would "disappear" when I turn and have a widescreen monitor. Lower width screens weren't affected. But, on my 24 inch widescreen monitor, I try to use up as much realestate as possible. Anyways, I added the following method to my PhysicObject.

public static bool IsInsideCone(Vector3 point, Vector3 coneOrigin, Vector3 coneDirection, double coneAngle)
{
            Vector3 tempVect = Vector3.Normalize(point - coneOrigin);
            return Vector3.Dot(coneDirection, tempVect) >= Math.Cos(coneAngle);
}


After that, I do a check in my PhysicObject.Draw method before drawing any meshes. My code for this looks like:

if (IsInsideCone(body.Position, camera.cameraPosition, camera.cameraDirection, FOVPlusPadding)){ }

My PhysicObject.Draw method now looks like this:

public override void Draw(GameTime gameTime)
{
            Camera camera = ((GameCore)this.Game).Camera;
            float FOVPlusPadding = camera.FOV * 1.1f;

            if (model != null && IsInsideCone(body.Position, camera.cameraPosition, camera.cameraDirection, FOVPlusPadding))
            {
                if (boneTransforms == null || boneCount != model.Bones.Count)
                {
                    boneTransforms = new Matrix[model.Bones.Count];
                    boneCount = model.Bones.Count;
                }

                model.CopyAbsoluteBoneTransformsTo(boneTransforms);

                    foreach (ModelMesh mesh in model.Meshes)
                    {
                        foreach (BasicEffect effect in mesh.Effects)
                        {
                            if (body.CollisionSkin != null)
                                effect.World = boneTransforms[mesh.ParentBone.Index] * Matrix.CreateScale(scale) * body.CollisionSkin.GetPrimitiveLocal(0).Transform.Orientation * body.Orientation * Matrix.CreateTranslation(body.Position);
                            else
                                effect.World = boneTransforms[mesh.ParentBone.Index] * Matrix.CreateScale(scale) * body.Orientation * Matrix.CreateTranslation(body.Position);

                            effect.View = camera.viewMatrix;
                            effect.Projection = camera.projectionMatrix;

                            ApplyEffects(effect);

                            effect.EnableDefaultLighting();
                            effect.PreferPerPixelLighting = true;
                        }
                        mesh.Draw();
                    }

            }
            if (this.collision != null)
            {
                int count = collision.NumPrimitives;
                for (int i = 0; i < count; i++)
                {
                    BoundingBox box = new BoundingBox();
                    BoundingBoxHelper.AddPrimitive(collision.GetPrimitiveOldWorld(i), ref box);
                }
            }

            base.Draw(gameTime);
}


Hopefully some of you out there in XNA will benefit from this code.
Jan 24, 2009 at 10:57 PM
Edited Jan 24, 2009 at 10:59 PM
Also, for testing purposes, I've added a distance check and only draw objects that are close to me. There's no point in drawing stuff that is on the other side of the map even if it is in my FOV cone. Just change the distance field to match how far away you want images to be drawn.

float distanceDiff = Vector3.Distance(body.Position, camera.cameraPosition);
float distance = 500.0f;
if (model != null && IsInsideCone(body.Position, camera.cameraPosition, camera.cameraDirection, FOVPlusPadding) && distanceDiff < distance) {