Example #1
0
        public static PerspectiveCamera CreateFromBounds(AxisAlignedBox3D bounds, Viewport3D viewport,
                                                         float fieldOfView, float yaw = 0.0f, float pitch = 0.0f, float zoom = 1.0f)
        {
            // Calculate initial guess at camera settings.
            Matrix3D          transform       = Matrix3D.CreateFromYawPitchRoll(yaw, pitch, 0);
            Vector3D          cameraDirection = Vector3D.Normalize(transform.Transform(Vector3D.Forward));
            PerspectiveCamera initialGuess    = new PerspectiveCamera
            {
                FieldOfView       = fieldOfView,
                NearPlaneDistance = 1.0f,
                FarPlaneDistance  = bounds.Size.Length() * 10,
                Position          = bounds.Center - cameraDirection * bounds.Size.Length() * 2,
                LookDirection     = cameraDirection,
                UpDirection       = Vector3D.Up
            };

            Matrix3D projection = initialGuess.GetProjectionMatrix(viewport.AspectRatio);
            Matrix3D view       = initialGuess.GetViewMatrix();

            // Project bounding box corners onto screen, and calculate screen bounds.
            float closestZ     = float.MaxValue;
            Box2D?screenBounds = null;

            Point3D[] corners = bounds.GetCorners();
            foreach (Point3D corner in corners)
            {
                Point3D screenPoint = viewport.Project(corner,
                                                       projection, view, Matrix3D.Identity);

                if (screenPoint.Z < closestZ)
                {
                    closestZ = screenPoint.Z;
                }

                IntPoint2D intScreenPoint = new IntPoint2D((int)screenPoint.X, (int)screenPoint.Y);
                if (screenBounds == null)
                {
                    screenBounds = new Box2D(intScreenPoint, intScreenPoint);
                }
                else
                {
                    Box2D value = screenBounds.Value;
                    value.Expand(intScreenPoint);
                    screenBounds = value;
                }
            }

            // Now project back from screen bounds into scene, setting Z to the minimum bounding box Z value.
            IntPoint2D minScreen = screenBounds.Value.Min;
            IntPoint2D maxScreen = screenBounds.Value.Max;
            Point3D    min       = viewport.Unproject(new Point3D(minScreen.X, minScreen.Y, closestZ),
                                                      projection, view, Matrix3D.Identity);
            Point3D max = viewport.Unproject(new Point3D(maxScreen.X, maxScreen.Y, closestZ),
                                             projection, view, Matrix3D.Identity);

            // Use these new values to calculate the distance the camera should be from the AABB centre.
            Vector3D size   = Vector3D.Abs(max - min);
            float    radius = size.Length();
            float    dist   = radius / (2 * MathUtility.Tan(fieldOfView * viewport.AspectRatio / 2));

            Point3D closestBoundsCenter = (min + (max - min) / 2);
            Point3D position            = closestBoundsCenter - cameraDirection * dist * (1 / zoom);

            return(new PerspectiveCamera
            {
                FieldOfView = fieldOfView,
                NearPlaneDistance = 1.0f,
                FarPlaneDistance = dist * 10,
                Position = position,
                LookDirection = cameraDirection,
                UpDirection = Vector3D.Up
            });
        }