public ContainmentType Contains(AxisAlignedBox3D box) { // FIXME: Is this a bug? // If the bounding box is of W * D * H = 0, then return disjoint if (box.Min == box.Max) { return(ContainmentType.Disjoint); } int i; ContainmentType contained; Point3D[] corners = box.GetCorners(); // First we assume completely disjoint. So if we find a point that is contained, we break out of this loop for (i = 0; i < corners.Length; i++) { if (Contains(corners[i]) != ContainmentType.Disjoint) { break; } } if (i == corners.Length) // This means we checked all the corners and they were all disjoint { return(ContainmentType.Disjoint); } if (i != 0) // if i is not equal to zero, we can fastpath and say that this box intersects { // because we know at least one point is outside and one is inside. return(ContainmentType.Intersects); } // If we get here, it means the first (and only) point we checked was actually contained in the frustum. // So we assume that all other points will also be contained. If one of the points is disjoint, we can // exit immediately saying that the result is Intersects i++; for (; i < corners.Length; i++) { if (Contains(corners[i]) != ContainmentType.Contains) { return(ContainmentType.Intersects); } } // If we get here, then we know all the points were actually contained, therefore result is Contains return(ContainmentType.Contains); }
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 }; }