Zooming Camera to an area

Jan 10, 2011 at 4:13 PM

I have created a behavior that allows me to draw a rectangle on the game surface and then send the coordinates of the rectangle to the game.  I would like to zoom the camera such that the view limits become the rectangle.  I am not sure exactly how to describe what I am trying to accomplish, except to refer to it as Zoom To Area.  In a sense, the rectangle should fill my viewport.

I have tried several ways to accomplish this.  The one that I have tried most recently is to set the target to be the center of the rectangle and then to test the coordinates of the corners of the rectangle using Camera.IsInView(Coordinate).


This will work.  However, what is failing is that I am using Ray/PickRay.  In order to use IsInView, I am trying to get the corner coordinates Viewport.GetNodeAtPosition and the nodes in my scene.  GetNodeAtPosition is returning the same node for all three calls.  here is what I am doing:

            double diffX = (bottomRight.X - topLeft.X) / 2.0;

            double diffY = (bottomRight.Y - topLeft.Y) / 2.0;


            Point halfWay = new Point(topLeft.X + diffX, topLeft.Y + diffY);


            Ray centerRay = Viewport.GetPickRay((int)halfWay.X, (int)halfWay.Y);

            var centerNode = Viewport.GetNodeAtPosition((int)halfWay.X, (int)halfWay.Y);


            Ray upperRay = Viewport.GetPickRay((int)topLeft.X, (int)topLeft.Y);

            var upperNode = Viewport.GetNodeAtPosition((int)topLeft.X, (int)topLeft.Y);

            Ray lowerRay = Viewport.GetPickRay((int)bottomRight.X, (int)bottomRight.Y);

            var lowerNode = Viewport.GetNodeAtPosition((int)bottomRight.X, (int)bottomRight.Y);

BottomRight and TopLeft are 2D Screen points.  When these lines execute, centerNode, lowerNode and upperNode are all identical, no matter how far apart the points are.


Any thoughts?

Jan 10, 2011 at 10:53 PM

I think I understand what you're trying to achieve, although I'm not absolutely certain. The term I think you're missing of Field of View. There's probably a much better definition somewhere, but I think of this as the angle between the camera and the top and bottom edges of the screen, and the left and right sides of the screen. It's the thing that changes when you zoom a physical camera.

So if you want to position the camera so that a given rectangle in 3d space fills the screen, and assuming the aspect ratios are the same, you need to calculate a normal to the rectangle which passes through the centre, and then position the camera along that normal pointing it at the center. The distance between the camera and the rectangle can be calculated from by simple trig from the field of view, and the size of the rectangle.

D'you see what I mean?

Jan 11, 2011 at 3:55 PM

I do see what you mean.  Can you send me an example of the calculation?

Jan 11, 2011 at 7:16 PM

No I can't - I've never needed to do that with Balder and I don't happen to have the code you need lying around. But it's conceptually straight forward. Calculate the position of the center of your rect; it's just the average of the four corners. Determine a vector which is perpendicular to your rect; that's going to involve some tedious arithmetic unless Balder/C# happen to provide library functions that can work this out for you. Calculate the distance from your camera to the rect; that's essentially  (rect.Width/2) * atan(camera.FieldOfView/2). Multiply the vector by your calculated distance and add it to the center of the rect and that gives you your camera position. Set the camera target to the center of the rect, and you're finished.

Jan 12, 2011 at 1:21 PM

I am assuming that when you say the vector perpendicular to the center of the rectangle you are meaning the unit normal at that point?  Or are you using any perpendicular vector at that point?

Jan 12, 2011 at 2:10 PM

One additional point is that I do not have the 3D coordinates of my rectangle, only 2d screen(Viewport) coordinates.  Any thoughts on how to get the 3D coords of the rectangle?  I can get my vector as a ray from the screen to an object in the view, but not the 3D position.

Jan 12, 2011 at 4:48 PM

I did mean unit normal, sorry for being imprecise. But if your rectangle is defined in 2D coordinates within the viewport, perhaps I've misunderstood what you're trying to achieve. Conceptually, you want to draw a rectangle inside the display area of the screen and then adjust the view so that the content of that rectangle fills the display area?

I can see two ways to achieve this, which give different effects.

You could simply apply a 2D transform to the view so that the relevant part of the display is stretched/moved to fill the display area. This is just a 2D rendering transform and wouldn't need to involve Balder at all. Note that the camera's perspective would not change: if the view shows an object towards the edge of the display area so that you were looking at it from the side, and you selected a rectangle centered on it, it would be bigger but still be drawn from the side.

Alternatively, move the camera so that it is centered over your selected rectangle with the target also set to the center of your rectangle; with this approach the perspective would change so that you appeared to be looking directly at the center of your selected area. To do this you would need to move the camera position and target by the same amount perpendicular to the forward vector so that the forward vector pointed at the center of your new rectangle, and then decide how far to move the camera towards the target so that your rectangle fills the screen. Note that with this approach since the perspective is changing there is no single 'right' answer for this second movement since your rectangle doesn't have a 3D plane associated with it; you would have to choose the 3D plane corresponding to the position of the object[s] you wanted to appear at the edge of the display area. Or, to describe the problem another way: when you move the camera sideways to position it over your selected rectangle, 3D objects will move a different distance on the screen depending how far they are away from the camera; you need to know how far away the point is that you want to appear at the edge of the display area. Perhaps you do already know this from the context you're selecting, but if not then you're asking a question which doesn't have a right answer.

Do either of these approaches match what you're really trying to achieve?