Math based question on how to use a second cam to view from above

hello all,

Basically I have set up a second cam to use as a sort of “minimap.” I know that you can make them as a gui element according to nifty, but I figured to just have a secondary display set up which imo would be easier to use.

The only issue I am running into is how high, or the “depth” should this cam be based on each scene? I know the “height” and “width” of the scene so I figured I could use some sort of math formula to figure it out.

I asked on a Math site I normally use and one user mentioned about “focal depth” which I assumed was the frustum?

He then mentioned I should use “similar triangles” as if the cam frustum could be used to find what I need?

I’m curious if anyone has any idea how I could solve this, or have I been given the answer already? If so I’m a bit confused…

Or if I am doing this completely wrong could someone let me know how ?I should go about correcting this issue?

Thanks!

So, the presumption is that what you are asking for is the camera distance that will fit your whole map in view?

You need to poke around in the camera’s projection matrix to sort this out, I think. Or at least that is one way to get your answer.

Using cam.getViewProjectionMatrix().m11 will let you calculate the distance needed to make a certain size in world space be a certain size in view space (This article I found pretty decent: http://msdn.microsoft.com/en-us/library/windows/desktop/ee418867(v=vs.85).aspx).

m11 should be (2 * Zn) / camHeight, where Zn is the ‘distance to the near plane’… but really we don’t have to worry about that.

I use this to figure out what distance I need if I want the units in world space at 0,0,0 to be 1:1 with the units in camera space (for a perspective HUD). But it’s essentially telling you how fast x,y view space varies with distance.

So if you know that your map is 10 units tall (5 extents) and you want that to fill the whole screen then you will need to be m11 * 5 units away.

Put another way:
distance = cam.getProjectionMatrix().m11 * Math.max(mapWorldWidth, mapWorldHeight) * 0.5;

I could be completely out of my mind, too.

Edit: forgot to divide Math.max() in half.

1 Like
@pspeed said: So, the presumption is that what you are asking for is the camera distance that will fit your whole map in view?

You need to poke around in the camera’s projection matrix to sort this out, I think. Or at least that is one way to get your answer.

Using cam.getViewProjectionMatrix().m11 will let you calculate the distance needed to make a certain size in world space be a certain size in view space (This article I found pretty decent: The Direct3D Transformation Pipeline - Win32 apps | Microsoft Learn).

m11 should be (2 * Zn) / camHeight, where Zn is the ‘distance to the near plane’… but really we don’t have to worry about that.

I use this to figure out what distance I need if I want the units in world space at 0,0,0 to be 1:1 with the units in camera space (for a perspective HUD). But it’s essentially telling you how fast x,y view space varies with distance.

So if you know that your map is 10 units tall (5 extents) and you want that to fill the whole screen then you will need to be m11 * 5 units away.

Put another way:
distance = cam.getProjectionMatrix().m11 * Math.max(mapWorldWidth, mapWorldHeight) * 0.5;

I could be completely out of my mind, too.

Edit: forgot to divide Math.max() in half.

Thanks a ton as always pspeed, I will try it out and see how it goes!! Interesting article as well, i will take a look at it.

Now normally how would a minimap be created? It seems like it would usually be a picture with some sort of location built in that would compare the world location to the minimap?

Edit: I tried both max and min, max is a bit too big overall, but min is basically as high as it should be(kind of cuts off part of the bottom and top) but the width is too long. which is weird since according tot he doc it says that it takes the smaller of the arguments which should be the height…?

@KonradZuse said: Thanks a ton as always pspeed, I will try it out and see how it goes!! Interesting article as well, i will take a look at it.

Now normally how would a minimap be created? It seems like it would usually be a picture with some sort of location built in that would compare the world location to the minimap?

Edit: I tried both max and min, max is a bit too big overall, but min is basically as high as it should be(kind of cuts off part of the bottom and top) but the width is too long. which is weird since according tot he doc it says that it takes the smaller of the arguments which should be the height…?

There are as many ways to create minimaps as there are types of games, I guess. Painted image, viewport in view of same scene, separate scene, whatever.

I’m not sure I understand your edit. If you used my code then what kind of values did you get for distance?

@pspeed said: There are as many ways to create minimaps as there are types of games, I guess. Painted image, viewport in view of same scene, separate scene, whatever.

I’m not sure I understand your edit. If you used my code then what kind of values did you get for distance?

Width: 300
Height: 200

Distance with Math.min = 485
Distance with Math.max = 725

Seems like min fits up I’ll take a s/s and edit.

Also, the presumption has been that you are using the projection matrix from the camera of the viewport.

Presuming the aspect rations of the map and the viewport are the same, doing the calculation with just height should be enough then.

Edit: and if the aspect rations are not similar then the math is a little more complicated than what I showed.

@pspeed said: Also, the presumption has been that you are using the projection matrix from the camera of the viewport.

Presuming the aspect rations of the map and the viewport are the same, doing the calculation with just height should be enough then.

Edit: and if the aspect rations are not similar then the math is a little more complicated than what I showed.

Yeah it should be exact, cam2.setViewPort(0.75f, 1.0f, 0.0f, 0.25f); is the size, and it makes sense with the height I guess, you would think it would be based on width since it’s bigger, but Idk it likes what it likes :p.

@KonradZuse said: Yeah it should be exact, cam2.setViewPort(0.75f, 1.0f, 0.0f, 0.25f); is the size, and it makes sense with the height I guess, you would think it would be based on width since it's bigger, but Idk it likes what it likes :p.

m11 is specifically related to height.

@pspeed said: m11 is specifically related to height.

Thank you!

So basically I have set everythingI need in regards to my cam, my next question has to do with clicking on the “minimap” (cam2) to cause actions to occur on my main screen (cam).

essentially I want to be able to click on the minimap, which would teleport me to that position in the main screen.

So essentially I know that my viewport is essentially 25^2% of my screen size.according to inputManager.getCursorPosition(); I can find the cursor position of the screen(from the bottom left), and since I know the size of my viewport I should be able take the scree position and figure out the position of my 100% screen?

I also have to factor in the difference from the bottom left as well.

Is this an appropriate approach, or is there another way you think would be besT?

Thanks as always,

~Jason

You can do picking in the small viewport just like you would in the main one. Depending on how it’s setup, yes, you may have to offset the coordinates when calculating the pick vectors but otherwise it’s the same process with just a different camera and different ‘scene’ you are picking.

@pspeed said: You can do picking in the small viewport just like you would in the main one. Depending on how it's setup, yes, you may have to offset the coordinates when calculating the pick vectors but otherwise it's the same process with just a different camera and different 'scene' you are picking.

I guess I thought about it forgetting about “Mouse Picking” so I will take a look at that in the docs.

Also, I wanted to know if you might know, but I made a thread here http://hub.jmonkeyengine.org/forum/topic/how-to-set-the-mouse-cursor-position/ asking if it’s possible to move the cursor manually to a position? I also wanted to know if it’s possible to then keep it bounded by that cam’s viewpoint aka I only want the mouse to be visible inside the cam2’s viewport, and if it collides it stops. I basically turn it on only to click the minimap.

Thanks again :slight_smile:

@KonradZuse said: I guess I thought about it forgetting about "Mouse Picking" so I will take a look at that in the docs.

Also, I wanted to know if you might know, but I made a thread here http://hub.jmonkeyengine.org/forum/topic/how-to-set-the-mouse-cursor-position/ asking if it’s possible to move the cursor manually to a position? I also wanted to know if it’s possible to then keep it bounded by that cam’s viewpoint aka I only want the mouse to be visible inside the cam2’s viewport, and if it collides it stops. I basically turn it on only to click the minimap.

Thanks again :slight_smile:

No, you’d have to manage your own cursor for that.

Sorry to come back to this after a few weeks, but I left this on hold while I did other things as I haven’t made any progress.

I’ve tried a few approaches, but nothing seems to work.

I think the best approach would be to click on the minimap, then send a ray down and see where it collides, but I’m having trouble converting the mouse position to the correct coordinates of the plane where my second cam is.

Any idea?

Thanks.

@KonradZuse said: Sorry to come back to this after a few weeks, but I left this on hold while I did other things as I haven't made any progress.

I’ve tried a few approaches, but nothing seems to work.

I think the best approach would be to click on the minimap, then send a ray down and see where it collides, but I’m having trouble converting the mouse position to the correct coordinates of the plane where my second cam is.

Any idea?

Thanks.

How have you setup the minimap? There are several very distinct answers depending on which of several approaches you’ve used.

@pspeed said: How have you setup the minimap? There are several very distinct answers depending on which of several approaches you've used.

I guess the Original Convo has been forgotten :P.

I’m using the second viewport/second cam as my mini map, then as we did earlier get the distance from the scene to the cam so we can have the scene in the entire second viewport.

I figured that I could just find out the location either by.

Clicking in the second cam, then shooting a ray down from the plane of the came, to the terrain.Once it hits the terrain it should know it’s position, but that’s based on my first cam then.

I tried things like taking the difference of the start( which is cam2.setViewPort(0.75f, 1.0f, 0.0f, 0.25f):wink: so bottom right cam.

In the mouse picking example, which I believe I mentioned above it takes the screen coordinate, and transforms it into a world coordinate. When I try to do the worldcoordinate of my cam2, for some reason it constantly is the same value, no matter where I clicked, it was weird.

I figured the ray would be the easiest approach, I also thought about that the top left edge is technically 0,0 in my scene, and that 0,0 of cam2 should equal cam1, but when cam2 is at the bottom right which in my screen’s case is 1920x1080 then it would be that value and not that value/4, value/4 (which is the size of my viewport, 25% each way).

I think I’m a bit confused on how the screencoordinates change to worldcoordinates(since the way I did it wasn’t changing it doesn’t make sense).

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:mouse_picking

[java]private AnalogListener analogListener = new AnalogListener() {
public void onAnalog(String name, float intensity, float tpf) {
if (name.equals(“pick target”)) {
// Reset results list.
CollisionResults results = new CollisionResults();
// Convert screen click to 3d position
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
// Aim the ray from the clicked spot forwards.
Ray ray = new Ray(click3d, dir);
// Collect intersections between ray and all nodes in results list.
rootNode.collideWith(ray, results);
// (Print the results so we see what is going on:)
for (int i = 0; i < results.size(); i++) {
// (For each “hit”, we know distance, impact point, geometry.)
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String target = results.getCollision(i).getGeometry().getName();
System.out.println(“Selection #” + i + ": " + target + " at " + pt + “, " + dist + " WU away.”);
}
// Use the results – we rotate the selected geometry.
if (results.size() > 0) {
// The closest result is the target that the player picked:
Geometry target = results.getClosestCollision().getGeometry();
// Here comes the action:
if (target.getName().equals(“Red Box”)) {
target.rotate(0, -intensity, 0);
} else if (target.getName().equals(“Blue Box”)) {
target.rotate(0, intensity, 0);
}
}
} // else if …
}
};[/java]

I figured this example would work out with some edits, but so far not so good…

I should be able to get the screen position of the second cam, convert it to world coordinates, then shoot the ray down and convert to cam1’s coordinates, but I guess I’m a bit confused on it all…

Thanks…

One way to double check some things might be to do a getScreenCoordinates() for some object in your scene that you know moves around in the map… do it from the viewport’s Camera. Then you can see if some corner-relative translation of the 2D mouse coordinate is needed before finding the 3D location.

Always using the viewport’s camera, of course.

@pspeed said: One way to double check some things might be to do a getScreenCoordinates() for some object in your scene that you know moves around in the map... do it from the viewport's Camera. Then you can see if some corner-relative translation of the 2D mouse coordinate is needed before finding the 3D location.

Always using the viewport’s camera, of course.

Nothing should be effecting it, but not too sure what you mean by “corner-relative translation of the 2d mouse coordinate.”

@KonradZuse said: Nothing should be effecting it, but not too sure what you mean by "corner-relative translation of the 2d mouse coordinate."

Mouse coordinates are in screen space and not viewport space. So you might have to subtract the x,y of the viewport’s corner.

@pspeed said: Mouse coordinates are in screen space and not viewport space. So you might have to subtract the x,y of the viewport's corner.

What I tried to do was use the top left of my viewport and it still got funky. A lot of it was at night, so I might have been too tired during the approach.

No idea why I have both of these up still (damn imps), but this is sort of what I was doing, note that it’s probably going to look a bit weird since I’m not sure where I stopped messing with either of them.

[java]

if(flyCam.clicked == true)
{
if(inputManager.getCursorPosition().getX() > this.settings.getWidth()*0.75 &&
inputManager.getCursorPosition().getY() < this.settings.getHeight()*0.25)
{
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam2.getWorldCoordinates(new Vector2f(click2d.x, this.settings.getHeight()-click2d.y), 0).clone();

                   System.out.println();
                   cam.setLocation(new Vector3f(click2d.x/4f,5f*ppi,(this.settings.getHeight()-click2d.y)/4f)) ;
       }
    flyCam.clicked = false;
   }
    if(flyCam.clicked == true)
   {
       Vector2f click2d = inputManager.getCursorPosition();
       Vector3f point= null;
       
       float w = this.settings.getWidth()* 0.75f;
       float h = this.settings.getHeight() *0.25f;

       if(click2d.x &gt;w &amp;&amp; click2d.y &lt; h)
       {

// Vector3f click3d = cam2.
//cam.setLocation(new Vector3f((click2d.x-w),0,-(click2d.y-h)));
}
flyCam.clicked = false;
}

[/java]

I set it first so that only clicks within the viewport react to my clicking (not sure if this is a correct approach or if I should be using appstates for something like that).

I basically have 2 modes, 1 that has no mouse, and then when pressing a button the mouse appears to do things like gui interaction or the mini map click.

I figured keeping everything together would work out best, but not too sure if there is another way.

I’m not sure what to tell you but something you said made me want to point this out, note the black text: