/// <summary>
        /// Transforms a box with a specified transform.
        /// </summary>
        /// <param name="inBox">The box to transform.</param>
        /// <param name="transform">The transform.</param>
        /// <returns>The transformed box.</returns>
        public static Box3F Transform(ref Box3F inBox, ref Matrix transform)
        {
            Vector3 center = 0.5f * (inBox.Min + inBox.Max);
            Vector3 extent = inBox.Max - center;

            Vector3 newExtent = new Vector3(
                    extent.X * Math.Abs(transform.M11) + extent.Y * Math.Abs(transform.M21) + extent.Z * Math.Abs(transform.M31),
                    extent.X * Math.Abs(transform.M12) + extent.Y * Math.Abs(transform.M22) + extent.Z * Math.Abs(transform.M32),
                    extent.X * Math.Abs(transform.M13) + extent.Y * Math.Abs(transform.M23) + extent.Z * Math.Abs(transform.M33));

            center = Vector3.Transform(center, transform);
            Box3F outBox = new Box3F(center - newExtent, center + newExtent);

            #if extra_strict_check
            // extra strict check
            {
                // Cycle through all the corners of the box and transform them.  Then
                // extend test box by transformed corners.  This box should match the
                // one we computed above except for precision errors.
                Box3F testBox= new Box3F(10E10f,10E10f,10E10f,-10E10f,-10E10f,-10E10f);
                center = 0.5f * (inBox.Min + inBox.Max);
                for (int i = 0; i < 8; i++)
                {
                    Vector3 corner = center;
                    corner.X += ((i & 1) == 0) ? extent.X : -extent.X;
                    corner.Y += ((i & 2) == 0) ? extent.Y : -extent.Y;
                    corner.Z += ((i & 4) == 0) ? extent.Z : -extent.Z;
                    corner = Vector3.Transform(corner, transform);
                    testBox.Extend(corner);
                }

                Assert.Fatal(Math.Abs(testBox.Min.X - outBox.Min.X) < 0.001f && Math.Abs(testBox.Min.Y - outBox.Min.Y) < 0.001f && Math.Abs(testBox.Min.Z - outBox.Min.Z) < 0.001f, "doh");
                Assert.Fatal(Math.Abs(testBox.Max.X - outBox.Max.X) < 0.001f && Math.Abs(testBox.Max.Y - outBox.Max.Y) < 0.001f && Math.Abs(testBox.Max.Z - outBox.Max.Z) < 0.001f, "doh");
            }
            #endif

            return outBox;
        }
        /// <summary>
        /// Generate the frustum.
        /// </summary>
        /// <param name="nearDist">The distance to the near plane.</param>
        /// <param name="farDist">The distance to the far plane.</param>
        /// <param name="fov">The field of view of the frustum.</param>
        /// <param name="aspectRatio">The aspect ratio.</param>
        /// <param name="camToWorld">The camera transform.</param>
        public void SetFrustum(float nearDist, float farDist, float fov, float aspectRatio, Matrix camToWorld)
        {
            _nearDist = nearDist;
            _farDist = farDist;
            float left = -farDist * (float)Math.Tan(fov * 0.5f) * aspectRatio;
            float right = -left;
            float bottom = left / aspectRatio;
            float top = -bottom;

            Vector3 farPosLeftUp = new Vector3(left, farDist, top);
            Vector3 farPosLeftDown = new Vector3(left, farDist, bottom);
            Vector3 farPosRightUp = new Vector3(right, farDist, top);
            Vector3 farPosRightDown = new Vector3(right, farDist, bottom);
            _cameraPos = camToWorld.Translation;
            _cameraY = MatrixUtil.MatrixGetRow(1, ref camToWorld);

            farPosLeftUp = MatrixUtil.MatMulP(ref farPosLeftUp, ref camToWorld);
            farPosLeftDown = MatrixUtil.MatMulP(ref farPosLeftDown, ref camToWorld);
            farPosRightUp = MatrixUtil.MatMulP(ref farPosRightUp, ref camToWorld);
            farPosRightDown = MatrixUtil.MatMulP(ref farPosRightDown, ref camToWorld);

            _bounds = new Box3F(_cameraPos, _cameraPos);
            _bounds.Extend(farPosLeftUp);
            _bounds.Extend(farPosLeftDown);
            _bounds.Extend(farPosRightUp);
            _bounds.Extend(farPosRightDown);

            _leftPlaneNormal = Vector3.Cross(farPosLeftUp - _cameraPos, farPosLeftDown - _cameraPos);
            _rightPlaneNormal = Vector3.Cross(farPosRightDown - _cameraPos, farPosRightUp - _cameraPos);
            _topPlaneNormal = Vector3.Cross(farPosRightUp - _cameraPos, farPosLeftUp - _cameraPos);
            _bottomPlaneNormal = Vector3.Cross(farPosLeftDown - _cameraPos, farPosRightDown - _cameraPos);
            _leftPlaneNormal.Normalize();
            _rightPlaneNormal.Normalize();
            _topPlaneNormal.Normalize();
            _bottomPlaneNormal.Normalize();
            _leftPlaneDist = Vector3.Dot(_leftPlaneNormal, _cameraPos);
            _rightPlaneDist = Vector3.Dot(_rightPlaneNormal, _cameraPos);
            _topPlaneDist = Vector3.Dot(_topPlaneNormal, _cameraPos);
            _bottomPlaneDist = Vector3.Dot(_bottomPlaneNormal, _cameraPos);
            _nearPlaneDist = -nearDist - Vector3.Dot(_cameraY, _cameraPos);
            _farPlaneDist = farDist + Vector3.Dot(_cameraY, _cameraPos);

            // precompute sin for quick object accept
            float minr = Math.Min(right, top);
            Assert.Fatal(minr > 0.0f, "doh!");
            _acceptCos = _farDist / (float)Math.Sqrt(_farDist * _farDist + minr * minr);
            _acceptInvSin = 1.0f / (float)Math.Sqrt(1.0f - _acceptCos * _acceptCos);

            // precompute sin for object culling
            float maxr = (float)Math.Sqrt(right * right + bottom * bottom); // anything outside this cone rejected
            _rejectCos = _farDist / (float)Math.Sqrt(_farDist * _farDist + maxr * maxr);
            _rejectInvSin = 1.0f / (float)Math.Sqrt(1.0f - _rejectCos * _rejectCos);
        }