private static Rect3D GetChildrenBoundsRecursive(ModelVisual3D visual, Matrix3D tx) { Rect3D bounds = Rect3D.Empty; Matrix3D currentTransform = MatrixUtils.Multiply(MatrixUtils.Value(visual.Transform), tx); if (visual.Content != null) { bounds = ModelBounder.CalculateBounds(visual.Content, currentTransform); } foreach (Visual3D v in visual.Children) { if (v is ModelVisual3D) { bounds.Union(GetChildrenBoundsRecursive((ModelVisual3D)v, currentTransform)); } } return(bounds); }
private void AdjustCamera(Camera camera, Model3DGroup scene) { ProjectionCamera pc = camera as ProjectionCamera; if (pc == null) { // Can't adjust MatrixCamera return; } if (double.IsNaN(pc.NearPlaneDistance) || double.IsNaN(pc.FarPlaneDistance) || double.IsPositiveInfinity(pc.NearPlaneDistance) || double.IsNegativeInfinity(pc.FarPlaneDistance) || pc.NearPlaneDistance > pc.FarPlaneDistance) { // Don't render, and don't use NaN. pc.NearPlaneDistance = double.MaxValue; pc.FarPlaneDistance = double.MaxValue; return; } bool adjustNearPlane = double.IsNegativeInfinity(pc.NearPlaneDistance); bool adjustFarPlane = double.IsPositiveInfinity(pc.FarPlaneDistance); if (!adjustNearPlane && !adjustFarPlane) { // Camera is fine. Leave it alone. return; } Matrix3D view = MatrixUtils.ViewMatrix(this.camera); Rect3D sceneBounds = ModelBounder.CalculateBounds(scene, view); if (sceneBounds.IsEmpty) { // It doesn't really matter what these are since there's nothing in the scene. // But I do like to avoid infinity... pc.NearPlaneDistance = double.MaxValue; pc.FarPlaneDistance = double.MaxValue; } else { // sceneBounds is aligned to the axes defined by the camera's view matrix, // but it's facing the wrong way! // // +-----------+ // |sceneBounds| // o---|-----------|---> look direction (-z axis) // | | // +-----------+ // ^ ^ // Z+sizeZ Z // // o is the camera position (also origin; z == 0) // This works regardless of the camera's position relative to the sceneBounds double np = pc.NearPlaneDistance; double fp = pc.FarPlaneDistance; double zBufferEpsilon = Math.Pow(2, -10); // Add a little padding to the scene bounds double sceneNearClip = -(sceneBounds.Z + sceneBounds.SizeZ + zBufferEpsilon); double sceneFarClip = -(sceneBounds.Z - zBufferEpsilon); // Shrink the user specified clipping planes to the tightest fit around // the scene without modifying what is visible. // Do not expand user's clipping plane values (unless we need a little more zBuffer tolerance)! if (adjustFarPlane) { pc.FarPlaneDistance = sceneFarClip; } if (adjustNearPlane) { pc.NearPlaneDistance = sceneNearClip; } if (pc.NearPlaneDistance > pc.FarPlaneDistance) { // Don't render. pc.NearPlaneDistance = double.MaxValue; pc.FarPlaneDistance = double.MaxValue; return; } if (pc is PerspectiveCamera && pc.NearPlaneDistance <= 0) { // We don't currently handle this case correctly. Just don't crash. pc.NearPlaneDistance = 0.125; } AdjustCameraTolerance(pc, adjustNearPlane, adjustFarPlane); } }