Commander Map
by Kyle Carter · 03/01/2004 (10:52 am) · 155 comments
Download Code File
To use this resource:
1. Copy guiCommanderHud.cc to your engine/gui subfolder.
2. Open your IDE of choice (or a text editor if that's what you like).
3. Add the source file to your project so it will be compiled into Torque.
4. Do a clean rebuild.
5. Go to the GUI of your choice and add a GuiCommanderHud to it. Poof, instant GUI!
Security and Scoping Issues:
The HUD is designed to prevent people from abusing it in-game. It does not change how anything is scoped, so people will only see what the net code would have previously allowed them to see. If you want to have a special "commander map" behavior that shows all active objects you will need to add it either via server side scripting or in GameConnection::doneScopingScene(). Remember, the best way to prevent cheating and improve net performance on your game is to only scope what people need to see.
If you're particularly paranoid, you may want to remove the ConsoleMethods at the end of the file and control the commander map with events; this is probably unnecessary in most cases, as the commander map reveals no more information than the player could get by opening a map in single player mode or a dedicated server and flying/walking around.
Panning and Zooming:
There are three useful actions you can do via scripting with the GuiCommanderHud. First, you can zoom. This basically consists of changing the FOV of the overhead camera that the view is being rendered from (see GuiCommanderHud::processCameraQuery()), so you want to stick to values between 0 and one half pi. Larger values can give "interesting" results though no value should crash the engine. The available script methods for this are zoom() to instantly set the zoom level and zoomTo() to smoothly interpolate to the requested zoom level.
pan() and panTo() let you set the x,y co-ordinates of the overhead camera - essentially setting where the center of the map is. If you wish to pan onto an object, get the first two words of its position and pass them to pan() or panTo().
There are also two exposed fields, panSpeed and zoomSpeed, which let you tweak how quickly panning and zooming occurs. Negative values for these will give catastrophic results!
Performance:
On a 1.8Ghz P4 with 512mb of RAM and a GeForce 4 440 Go (equivalent to a fast GeForce 2), I get 70fps in the test FPS level without the map on screen, and 40fps with it on the screen (in addition to the normal FPS view). Suggested usage is to run the GuiCommanderHud in its own GUI, so that you don't have the overhead of the PlayGUI and its view of the world.
The GuiCommanderHud only renders interior, terrain, water, and environmental objects. You can easily change the mask it uses for rendering the scene by editing GuiCommanderHud::renderWorld() - there is a comment to guide you. Re-enabling all DTS shapes will bring a significant performance hit, since you'll be rendering potentially all the objects in the game world!
Extensions:
There are three major areas in which the GuiCommanderHud could be extended for your game.
First, you may want to render an overlay or icons indicating where objectives/players are. There is a comment located where you would want to render these objects, in GuiCommanderHud::onRender(). Using standard DGL calls will give good results.
Second, you may want to have the control handle mouse input. Looking at EditTSCtrl (located in engine/editor/editTSCtrl.*) will be an excellent guide for this process. You could either inherit from EditTSCtrl, or copy its mouse handling code to the GuiCommanderHud. The project() and unproject() functions that GuiCommmanderHud inherits from GuiTSCtrl may also be useful here.
Finally, you may wish to optimize rendering speed by caching output to a texture, rendering only the texture, and updating the texture as needed when the map needs to change its view. (You could even render to a large texture and simply pan it around, though this would kill the neat parallaxing and depth buffering effects that you could gain from actually rendering the world into the GuiCommanderHud.) This would be a bit tricky, but GuiCommanderHud::onRender would be your starting point. Look at TSShapeInstance::snapshot() for guidance on how to implement this sort of functionality in OpenGL - beware that the DirectX layer may not place nice with this.
Usage Restrictions:
None. Knock yourself out! :) I would ask that you put my name in your credits somewhere and send me a free copy of your game if this resource is a major piece of functionality for your project, but you are by no means required to do so. I'd also appreciate it if, if you found bugs or made enhancements for the code, if you'd send me your change(s) so I can merge them back into this resource. Many eyes find more bugs. ;)
Addendums:
Neat bit of code to try from the console(replace 1494 with the SimObject you want to track). Notice my commander hud is named commHud.
To use this resource:
1. Copy guiCommanderHud.cc to your engine/gui subfolder.
2. Open your IDE of choice (or a text editor if that's what you like).
3. Add the source file to your project so it will be compiled into Torque.
4. Do a clean rebuild.
5. Go to the GUI of your choice and add a GuiCommanderHud to it. Poof, instant GUI!
Security and Scoping Issues:
The HUD is designed to prevent people from abusing it in-game. It does not change how anything is scoped, so people will only see what the net code would have previously allowed them to see. If you want to have a special "commander map" behavior that shows all active objects you will need to add it either via server side scripting or in GameConnection::doneScopingScene(). Remember, the best way to prevent cheating and improve net performance on your game is to only scope what people need to see.
If you're particularly paranoid, you may want to remove the ConsoleMethods at the end of the file and control the commander map with events; this is probably unnecessary in most cases, as the commander map reveals no more information than the player could get by opening a map in single player mode or a dedicated server and flying/walking around.
Panning and Zooming:
There are three useful actions you can do via scripting with the GuiCommanderHud. First, you can zoom. This basically consists of changing the FOV of the overhead camera that the view is being rendered from (see GuiCommanderHud::processCameraQuery()), so you want to stick to values between 0 and one half pi. Larger values can give "interesting" results though no value should crash the engine. The available script methods for this are zoom() to instantly set the zoom level and zoomTo() to smoothly interpolate to the requested zoom level.
pan() and panTo() let you set the x,y co-ordinates of the overhead camera - essentially setting where the center of the map is. If you wish to pan onto an object, get the first two words of its position and pass them to pan() or panTo().
There are also two exposed fields, panSpeed and zoomSpeed, which let you tweak how quickly panning and zooming occurs. Negative values for these will give catastrophic results!
Performance:
On a 1.8Ghz P4 with 512mb of RAM and a GeForce 4 440 Go (equivalent to a fast GeForce 2), I get 70fps in the test FPS level without the map on screen, and 40fps with it on the screen (in addition to the normal FPS view). Suggested usage is to run the GuiCommanderHud in its own GUI, so that you don't have the overhead of the PlayGUI and its view of the world.
The GuiCommanderHud only renders interior, terrain, water, and environmental objects. You can easily change the mask it uses for rendering the scene by editing GuiCommanderHud::renderWorld() - there is a comment to guide you. Re-enabling all DTS shapes will bring a significant performance hit, since you'll be rendering potentially all the objects in the game world!
Extensions:
There are three major areas in which the GuiCommanderHud could be extended for your game.
First, you may want to render an overlay or icons indicating where objectives/players are. There is a comment located where you would want to render these objects, in GuiCommanderHud::onRender(). Using standard DGL calls will give good results.
Second, you may want to have the control handle mouse input. Looking at EditTSCtrl (located in engine/editor/editTSCtrl.*) will be an excellent guide for this process. You could either inherit from EditTSCtrl, or copy its mouse handling code to the GuiCommanderHud. The project() and unproject() functions that GuiCommmanderHud inherits from GuiTSCtrl may also be useful here.
Finally, you may wish to optimize rendering speed by caching output to a texture, rendering only the texture, and updating the texture as needed when the map needs to change its view. (You could even render to a large texture and simply pan it around, though this would kill the neat parallaxing and depth buffering effects that you could gain from actually rendering the world into the GuiCommanderHud.) This would be a bit tricky, but GuiCommanderHud::onRender would be your starting point. Look at TSShapeInstance::snapshot() for guidance on how to implement this sort of functionality in OpenGL - beware that the DirectX layer may not place nice with this.
Usage Restrictions:
None. Knock yourself out! :) I would ask that you put my name in your credits somewhere and send me a free copy of your game if this resource is a major piece of functionality for your project, but you are by no means required to do so. I'd also appreciate it if, if you found bugs or made enhancements for the code, if you'd send me your change(s) so I can merge them back into this resource. Many eyes find more bugs. ;)
Addendums:
Neat bit of code to try from the console(replace 1494 with the SimObject you want to track). Notice my commander hud is named commHud.
commHud.zoomTo(0.02);
function panPlayer() { commHud.panTo(getWord(1494.getPosition(), 0), getWord(1494.getPosition(), 1)); schedule(100, 0, panPlayer); }
panPlayer();
#122
Do I need to call project() before rendering the grid, and if so what parameters should I pass in?
05/18/2007 (7:15 am)
That was a var left over from the WorldEditor code. It plays a role in how the grid gets drawn.Do I need to call project() before rendering the grid, and if so what parameters should I pass in?
#123
If anyone is interested in the project and can help, you can either reply in this thread, or email me at:
andy@gameshout.com
Thanks.
05/18/2007 (7:46 am)
I was wondering if anyone has this thing working solid in TGEA? I'm willing to pay money to have it converted. I had it converted prior to TGEA v1.01 but don't know what happened.If anyone is interested in the project and can help, you can either reply in this thread, or email me at:
andy@gameshout.com
Thanks.
#124
Hope that helps, I'll have a look later at getting the grid working
05/18/2007 (7:57 am)
@Michael - yes you will need to call project which converts coordinates from worldspace into screen space, for example to draw a line starting at world position 300, 300 and extending out to the right for 500;Point3F start(300, 300, 0); Point3F screenpos; project(start, &screenpos); glBegin(GL_LINE_LOOP); glVertex2f(screenpos.x, screenpos.y); glVertex2f(screenpos.x + 500, screenpos.y); glEnd();
Hope that helps, I'll have a look later at getting the grid working
#125
Unfortunately whilst it returns values they're not correct, further more if I vary the Z-coord in screenpos from 0,1, etc it completely alters the worldpos.x and worldpos.y coords returned so I am assuming that's why my results are incorrect.
Can anyone tell me what I should be using as the z-coord in screenpos to return the right results?
Thanks
05/21/2007 (4:23 am)
I'm struggling with getting the unproject() call to work, basically when drawing grid lines I want to get the world space coords for the top left of the command map so that I'm only trying to draw the grid lines that would be visible, so I'm using this call:Point3F screenpos(mBounds.point.x, mBounds.point.y, 0); Point3F worldpos; unproject(screenpos, &worldpos);
Unfortunately whilst it returns values they're not correct, further more if I vary the Z-coord in screenpos from 0,1, etc it completely alters the worldpos.x and worldpos.y coords returned so I am assuming that's why my results are incorrect.
Can anyone tell me what I should be using as the z-coord in screenpos to return the right results?
Thanks
#126
something about player dots???
My we have some code to have a start from???
tnx
05/22/2007 (7:57 am)
Andy saw your post earliersomething about player dots???
My we have some code to have a start from???
tnx
#127
Once it's finished and I've tidied up the code then I'll be releasing it as a resource for the community to use but I expect it will be a week or two depending on how easily I can solve this problem.
05/23/2007 (5:03 am)
@blackcode - Unfortunately it's in a bit of a mess at the moment, I had it working but wasn't satisfied with the drop in frame rate you get from the commander map, profiling showed around 22% of the time was being spent on rendering the map and have since ripped it apart to render the map to an image and only refresh it on a period basis, works like a charm and a huge improvement on frame rates - at the moment I'm just working on getting the rotation and capturing the map as a larger image than displayed (therefore requiring to refresh it less frequently), struggling a little to be honest with that part.Once it's finished and I've tidied up the code then I'll be releasing it as a resource for the community to use but I expect it will be a week or two depending on how easily I can solve this problem.
#128
05/23/2007 (5:20 am)
@Andy - If you could please send me an e-mail, I have something to show you based on this resource that you might find helpful. (mich.perry@gmail.com)
#129
05/24/2007 (8:52 am)
@Michael - I don't know if my emails are getting through to you, or vice versa. Sent you an email this morning. Let me know. Thanks!
#130
05/24/2007 (9:05 am)
#131
-Andy
06/12/2007 (10:02 am)
May have TGEA version of this shortly. I am currently working with 2 other devs and we think we've worked out the kinks. Will post resource when finished.-Andy
#132
06/12/2007 (10:17 am)
Any chances of player icons? :D
#133
edit: code looks even more like a mess in the post lol
07/03/2007 (5:34 pm)
I can't get the unproject to work. Project works fine and i now have some nice player icons but i want to get a world pos from a screen pos when i click on the mapGui. I tryed several things and my codes lookes really messed up atmvoid GuiMapHud::onMouseDown(const GuiEvent &event)
{
MatrixF mat;
Point3F vel;
//if ( GameGetCameraTransform(&mat, &vel) )
if ( mCameraMat )
{
Point3F pos;
mCameraMat.getColumn(3,&pos);
const Point3F screenPoint(event.mousePoint.x, event.mousePoint.y, pos.z);
Point3F worldPoint;
if (unproject(screenPoint, &worldPoint))
{
//Con::printf("moving mouse (%f, %f)",event.mousePoint.x,event.mousePoint.y);
//Con::printf("moving mouse (%f, %f)",worldPoint.x,worldPoint.y);
if(1){
Point3F vec = worldPoint - pos;
Point3F lineTestStart = pos;
vec.normalizeSafe();
Point3F lineTestEnd = pos + vec * 1000 * 500;
Con::printf("LineTestEnd (%f, %f)",lineTestEnd.x,lineTestEnd.y);
Con::executef(this, 3, "onMouseDown", Con::getFloatArg(lineTestEnd.x),Con::getFloatArg(lineTestEnd.y));
}
if(0)
{
Con::printf("LineTestEnd (%f, %f)",worldPoint.x,worldPoint.y);
Con::executef(this, 3, "onMouseDown", Con::getFloatArg(worldPoint.x),Con::getFloatArg(worldPoint.y));
}
}
}
}I searched the engine for other unproject calls, found only 2, but didn't help much. I would appreciate if anybody could share some information how it should be done :) btw i'm using torque 1.4.2edit: code looks even more like a mess in the post lol
#135
BTW, I had no propblems with this resource in TGE 1.5.2 after a few fixes.
08/16/2007 (12:39 am)
I'm not a noob but I can't seem to get the "cut n paste" down... How do I cut and paste out of a code dialog box because if I do that it just ends up as one line. This has evaded me for years and I understand why it does it but, how do you simply fix it when copying these code boxes.BTW, I had no propblems with this resource in TGE 1.5.2 after a few fixes.
#136
08/16/2007 (4:56 am)
@Chad: Paste it into Wordpad (not Notepad) first ;-)
#137
@Andy Hodges
Point3F start(300, 300, 0);
Point3F screenpos;
project(start, &screenpos);
glBegin(GL_LINE_LOOP);
glVertex2f(screenpos.x, screenpos.y);
glVertex2f(screenpos.x + 500, screenpos.y);
glEnd();
no matter how I tried, the "project(start, &screenpos);" always return a false.
09/25/2007 (11:02 pm)
Has anyone gotten the project method to work?@Andy Hodges
Point3F start(300, 300, 0);
Point3F screenpos;
project(start, &screenpos);
glBegin(GL_LINE_LOOP);
glVertex2f(screenpos.x, screenpos.y);
glVertex2f(screenpos.x + 500, screenpos.y);
glEnd();
no matter how I tried, the "project(start, &screenpos);" always return a false.
#138
bool GuiTSCtrl::project(const Point3F &pt, Point3F *dest)
{
GLdouble winx, winy, winz;
GLint result = gluProject(pt.x, pt.y, pt.z,
mSaveModelview, mSaveProjection, mSaveViewport,
&winx, &winy, &winz);
Con::printf("winx=%f,winy=%f,winz=%f",winx,winy,winz);
if(result == GL_FALSE || winz < 0 || winz > 1)
return false;
dest->set(winx, winy, winz);
return true;
}
I find that the winz always bigger than 1, so it return false.
Can someone give me some suggestion. Thanks.
09/25/2007 (11:14 pm)
I track into the project method:bool GuiTSCtrl::project(const Point3F &pt, Point3F *dest)
{
GLdouble winx, winy, winz;
GLint result = gluProject(pt.x, pt.y, pt.z,
mSaveModelview, mSaveProjection, mSaveViewport,
&winx, &winy, &winz);
Con::printf("winx=%f,winy=%f,winz=%f",winx,winy,winz);
if(result == GL_FALSE || winz < 0 || winz > 1)
return false;
dest->set(winx, winy, winz);
return true;
}
I find that the winz always bigger than 1, so it return false.
Can someone give me some suggestion. Thanks.
#139
10/14/2007 (8:26 am)
Meh... how can i get this to render my players also? Good Resource non the less.
#140
" // If you want to render other things, change this mask."
So to render the players change that mask to the following
or to render an icon on top of the player add this in the onRender method below the comment
10/15/2007 (2:41 am)
in 'void GuiMapHud::renderWorld(const RectI &updateRect)' there is a comment:" // If you want to render other things, change this mask."
So to render the players change that mask to the following
gClientSceneGraph->renderScene( EnvironmentObjectType | TerrainObjectType | InteriorObjectType | WaterObjectType | PlayerObjectType );
or to render an icon on top of the player add this in the onRender method below the comment
// If you wanted to render custom GUI elements, like a sensor map, icons for // players/vehicles/objectives, you would do it here by calling project() // for all their positions and drawing bitmaps at the appropriate locations.
// Must have a connection
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn) return;
// Must have controlled object
ShapeBase* control = conn->getControlObject();
if (!control) return;
glDisable(GL_TEXTURE_2D); // Plot the Dots...
glDisable(GL_BLEND);
glPointSize(3.0);
glEnable(GL_POINT_SMOOTH);
glBegin(GL_POINTS);
glColor4f(1.0, 0, 1.0, 1.0); // Center Point
// Go through all ghosted objects on connection (client-side)
for (SimSetIterator itr(conn); *itr; ++itr) {
// Make sure that the object is a shapebase object
if ((*itr)->getType() & ShapeBaseObjectType) {
ShapeBase* shape = static_cast<ShapeBase*>(*itr);
// Make sure that the object isn't the client
if (/*shape != control &&*/ shape->getShapeName()) {
// Make sure the shapebase object is a player
if (shape->getType() & PlayerObjectType) {
Point3F newCoord;
// Get coords of player object
newCoord = shape->getPosition();
Point3F DestCoord;
if (!project(newCoord, &DestCoord))
continue;
glVertex2f(DestCoord.x,DestCoord.y);
}
}
}
}
glEnd(); 
Torque Owner Andy Rollins
ZDay Game