]> git.mxchange.org Git - flightgear.git/blob - src/GUI/gui_funcs.cxx
add a command to print information about visible objects in the scene
[flightgear.git] / src / GUI / gui_funcs.cxx
1 /**************************************************************************
2  * gui_funcs.cxx
3  *
4  * Based on gui.cxx and renamed on 2002/08/13 by Erik Hofman.
5  *
6  * Written 1998 by Durk Talsma, started Juni, 1998.  For the flight gear
7  * project.
8  *
9  * Additional mouse supported added by David Megginson, 1999.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24  *
25  * $Id$
26  **************************************************************************/
27
28
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32
33 #include <simgear/compiler.h>
34
35 #include <fstream>
36 #include <string>
37 #include <cstring>
38 #include <sstream>
39
40 #include <stdlib.h>
41
42 #include <simgear/debug/logstream.hxx>
43 #include <simgear/misc/sg_path.hxx>
44 #include <simgear/screen/screen-dump.hxx>
45
46 #include <Cockpit/panel.hxx>
47 #include <Main/globals.hxx>
48 #include <Main/fg_props.hxx>
49 #include <Main/fg_os.hxx>
50 #include <Main/renderer.hxx>
51 #include <Main/viewmgr.hxx>
52 #include <GUI/new_gui.hxx>
53
54 #ifdef _WIN32
55 #  include <shellapi.h>
56 #endif
57
58 #include "gui.h"
59
60 using std::string;
61
62
63 #if defined( TR_HIRES_SNAP)
64 #include <simgear/screen/tr.h>
65 extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
66                          GLfloat x_end, GLfloat y_end );
67 #endif
68
69
70 const __fg_gui_fn_t __fg_gui_fn[] = {
71 #ifdef TR_HIRES_SNAP
72         {"dumpHiResSnapShot", fgHiResDumpWrapper},
73 #endif
74         {"dumpSnapShot", fgDumpSnapShotWrapper},
75         // Help
76         {"helpCb", helpCb},
77
78         // Structure termination
79         {"", NULL}
80 };
81
82
83 /* ================ General Purpose Functions ================ */
84
85 // General Purpose Message Box. Makes sure no more than 5 different
86 // messages are displayed at the same time, and none of them are
87 // duplicates. (5 is a *lot*, but this will hardly ever be reached
88 // and we don't want to miss any, either.)
89 void mkDialog (const char *txt)
90 {
91     NewGUI *gui = (NewGUI *)globals->get_subsystem("gui");
92     if (!gui)
93         return;
94     SGPropertyNode *master = gui->getDialogProperties("message");
95     if (!master)
96         return;
97
98     const int maxdialogs = 5;
99     string name;
100     SGPropertyNode *msg = fgGetNode("/sim/gui/dialogs", true);
101     int i;
102     for (i = 0; i < maxdialogs; i++) {
103         std::ostringstream s;
104         s << "message-" << i;
105         name = s.str();
106
107         if (!msg->getNode(name.c_str(), false))
108             break;
109
110         if (!strcmp(txt, msg->getNode(name.c_str())->getStringValue("message"))) {
111             SG_LOG(SG_GENERAL, SG_WARN, "mkDialog(): duplicate of message " << txt);
112             return;
113         }
114     }
115     if (i == maxdialogs)
116         return;
117     msg = msg->getNode(name.c_str(), true);
118     msg->setStringValue("message", txt);
119     msg = msg->getNode("dialog", true);
120     copyProperties(master, msg);
121     msg->setStringValue("name", name.c_str());
122     gui->newDialog(msg);
123     gui->showDialog(name.c_str());
124 }
125
126 // Message Box to report an error.
127 void guiErrorMessage (const char *txt)
128 {
129     SG_LOG(SG_GENERAL, SG_ALERT, txt);
130     mkDialog(txt);
131 }
132
133 // Message Box to report a throwable (usually an exception).
134 void guiErrorMessage (const char *txt, const sg_throwable &throwable)
135 {
136     string msg = txt;
137     msg += '\n';
138     msg += throwable.getFormattedMessage();
139     if (!std::strlen(throwable.getOrigin()) != 0) {
140         msg += "\n (reported by ";
141         msg += throwable.getOrigin();
142         msg += ')';
143     }
144     SG_LOG(SG_GENERAL, SG_ALERT, msg);
145     mkDialog(msg.c_str());
146 }
147
148
149
150 /* -----------------------------------------------------------------------
151 the Gui callback functions 
152 ____________________________________________________________________*/
153
154
155 // Hier Neu :-) This is my newly added code
156 // Added by David Findlay <nedz@bigpond.com>
157 // on Sunday 3rd of December
158
159
160 void helpCb ()
161 {
162     string command;
163         
164     SGPath path( globals->get_fg_root() );
165     path.append( "Docs/index.html" );
166         
167 #ifndef _WIN32
168
169     command = globals->get_browser();
170     string::size_type pos;
171     if ((pos = command.find("%u", 0)) != string::npos)
172         command.replace(pos, 2, path.str());
173     else
174         command += " " + path.str();
175
176     command += " &";
177     system( command.c_str() );
178
179 #else // _WIN32
180
181     // Look for favorite browser
182     char Dummy[1024], ExecName[1024], browserParameter[1024];
183     char win32_name[1024];
184 # ifdef __CYGWIN__
185     cygwin32_conv_to_full_win32_path(path.c_str(),win32_name);
186 # else
187     strncpy(win32_name,path.c_str(), 1024);
188 # endif
189     Dummy[0] = 0;
190     FindExecutable(win32_name, Dummy, ExecName);
191     snprintf(browserParameter, 1024, "file:///%s", win32_name);
192     ShellExecute ( NULL, "open", ExecName, browserParameter, Dummy,
193                    SW_SHOWNORMAL ) ;
194
195 #endif
196         
197     mkDialog ("Help started in your web browser window.");
198 }
199
200 #if defined( TR_HIRES_SNAP)
201 void fgHiResDump()
202 {
203     FILE *f;
204     string message;
205     bool menu_status = fgGetBool("/sim/menubar/visibility");
206     char *filename = new char [24];
207     static int count = 1;
208
209     static const SGPropertyNode *master_freeze
210         = fgGetNode("/sim/freeze/master");
211
212     bool freeze = master_freeze->getBoolValue();
213     if ( !freeze ) {
214         fgSetBool("/sim/freeze/master", true);
215     }
216
217     fgSetBool("/sim/menubar/visibility", false);
218     int mouse = fgGetMouseCursor();
219     fgSetMouseCursor(MOUSE_CURSOR_NONE);
220
221     FGRenderer *renderer = globals->get_renderer();
222 //     renderer->init();
223     renderer->resize( fgGetInt("/sim/startup/xsize"),
224                       fgGetInt("/sim/startup/ysize") );
225
226     // we need two render frames here to clear the menu and cursor
227     // ... not sure why but doing an extra fgRenderFrame() shouldn't
228     // hurt anything
229     //renderer->update( true );
230     //renderer->update( true );
231
232     // This ImageSize stuff is a temporary hack
233     // should probably use 128x128 tile size and
234     // support any image size
235
236     // This should be a requester to get multiplier from user
237     int multiplier = fgGetInt("/sim/startup/hires-multiplier", 3);
238     int width = fgGetInt("/sim/startup/xsize");
239     int height = fgGetInt("/sim/startup/ysize");
240         
241     /* allocate buffer large enough to store one tile */
242     GLubyte *tile = (GLubyte *)malloc(width * height * 3 * sizeof(GLubyte));
243     if (!tile) {
244         delete [] filename;
245         printf("Malloc of tile buffer failed!\n");
246         return;
247     }
248
249     int imageWidth  = multiplier*width;
250     int imageHeight = multiplier*height;
251
252     /* allocate buffer to hold a row of tiles */
253     GLubyte *buffer
254         = (GLubyte *)malloc(imageWidth * height * 3 * sizeof(GLubyte));
255     if (!buffer) {
256         delete [] filename;
257         free(tile);
258         printf("Malloc of tile row buffer failed!\n");
259         return;
260     }
261     TRcontext *tr = trNew();
262     trTileSize(tr, width, height, 0);
263     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
264     trImageSize(tr, imageWidth, imageHeight);
265     trRowOrder(tr, TR_TOP_TO_BOTTOM);
266     // OSGFIXME
267 //     sgFrustum *frustum = ssgGetFrustum();
268 //     trFrustum(tr,
269 //               frustum->getLeft(), frustum->getRight(),
270 //               frustum->getBot(),  frustum->getTop(), 
271 //               frustum->getNear(), frustum->getFar());
272         
273     /* Prepare ppm output file */
274     while (count < 1000) {
275         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
276         if ( (f = fopen(filename, "r")) == NULL )
277             break;
278         fclose(f);
279     }
280     f = fopen(filename, "wb");
281     if (!f) {
282         printf("Couldn't open image file: %s\n", filename);
283         delete [] filename;
284         free(buffer);
285         free(tile);
286         return;
287     }
288     fprintf(f,"P6\n");
289     fprintf(f,"# ppm-file created by %s\n", "trdemo2");
290     fprintf(f,"%i %i\n", imageWidth, imageHeight);
291     fprintf(f,"255\n");
292
293     /* just to be safe... */
294     glPixelStorei(GL_PACK_ALIGNMENT, 1);
295
296     // OSGFIXME
297 #if 0
298     /* Because the HUD and Panel change the ViewPort we will
299      * need to handle some lowlevel stuff ourselves */
300     int ncols = trGet(tr, TR_COLUMNS);
301     int nrows = trGet(tr, TR_ROWS);
302
303     bool do_hud = fgGetBool("/sim/hud/visibility");
304     GLfloat hud_col_step = 640.0 / ncols;
305     GLfloat hud_row_step = 480.0 / nrows;
306         
307     bool do_panel = fgPanelVisible();
308     GLfloat panel_col_step = globals->get_current_panel()->getWidth() / ncols;
309     GLfloat panel_row_step = globals->get_current_panel()->getHeight() / nrows;
310 #endif
311     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
312     glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
313     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
314     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
315     glHint(GL_FOG_HINT, GL_NICEST);
316         
317     /* Draw tiles */
318     int more = 1;
319     while (more) {
320         trBeginTile(tr);
321         int curColumn = trGet(tr, TR_CURRENT_COLUMN);
322         // int curRow =  trGet(tr, TR_CURRENT_ROW);
323
324         renderer->update( false );
325         // OSGFIXME
326 //         if ( do_hud )
327 //             fgUpdateHUD( curColumn*hud_col_step,      curRow*hud_row_step,
328 //                          (curColumn+1)*hud_col_step, (curRow+1)*hud_row_step );
329         // OSGFIXME
330 //         if (do_panel)
331 //             globals->get_current_panel()->update(
332 //                                    curColumn*panel_col_step, panel_col_step,
333 //                                    curRow*panel_row_step,    panel_row_step );
334         more = trEndTile(tr);
335
336         /* save tile into tile row buffer*/
337         int curTileWidth = trGet(tr, TR_CURRENT_TILE_WIDTH);
338         int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
339         int bytesPerTileRow = (width) * 3*sizeof(GLubyte);
340         int xOffset = curColumn * bytesPerTileRow;
341         int bytesPerCurrentTileRow = (curTileWidth) * 3*sizeof(GLubyte);
342         int i;
343         for (i=0;i<height;i++) {
344             memcpy(buffer + i*bytesPerImageRow + xOffset, /* Dest */
345                    tile + i*bytesPerTileRow,              /* Src */
346                    bytesPerCurrentTileRow);               /* Byte count*/
347         }
348
349         if (curColumn == trGet(tr, TR_COLUMNS)-1) {
350             /* write this buffered row of tiles to the file */
351             int curTileHeight = trGet(tr, TR_CURRENT_TILE_HEIGHT);
352             int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
353             int i;
354             for (i=0;i<curTileHeight;i++) {
355                 /* Remember, OpenGL images are bottom to top.  Have to reverse. */
356                 GLubyte *rowPtr = buffer + (curTileHeight-1-i) * bytesPerImageRow;
357                 fwrite(rowPtr, 1, imageWidth*3, f);
358             }
359         }
360
361     }
362
363     renderer->resize( width, height );
364
365     trDelete(tr);
366
367     glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
368     glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
369     glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
370     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
371     if ( (!strcmp(fgGetString("/sim/rendering/fog"), "disabled")) ||
372          (!fgGetBool("/sim/rendering/shading"))) {
373         // if fastest fog requested, or if flat shading force fastest
374         glHint ( GL_FOG_HINT, GL_FASTEST );
375     } else if ( !strcmp(fgGetString("/sim/rendering/fog"), "nicest") ) {
376         glHint ( GL_FOG_HINT, GL_DONT_CARE );
377     }
378
379     fclose(f);
380
381     message = "Snapshot saved to \"";
382     message += filename;
383     message += "\".";
384     mkDialog (message.c_str());
385
386     free(tile);
387     free(buffer);
388
389     delete [] filename;
390
391     fgSetMouseCursor(mouse);
392     fgSetBool("/sim/menubar/visibility", menu_status);
393
394     if ( !freeze ) {
395         fgSetBool("/sim/freeze/master", false);
396     }
397 }
398 #endif // #if defined( TR_HIRES_SNAP)
399
400 void fgDumpSnapShotWrapper () {
401     fgDumpSnapShot();
402 }
403
404
405 void fgHiResDumpWrapper () {
406     fgHiResDump();
407 }
408
409
410 // do a screen snap shot
411 bool fgDumpSnapShot () {
412     static SGConstPropertyNode_ptr master_freeze = fgGetNode("/sim/freeze/master");
413
414     bool freeze = master_freeze->getBoolValue();
415     if ( !freeze ) {
416         fgSetBool("/sim/freeze/master", true);
417     }
418
419     int mouse = fgGetMouseCursor();
420     fgSetMouseCursor(MOUSE_CURSOR_NONE);
421
422     fgSetBool("/sim/signals/screenshot", true);
423
424     FGRenderer *renderer = globals->get_renderer();
425     renderer->resize( fgGetInt("/sim/startup/xsize"),
426                       fgGetInt("/sim/startup/ysize") );
427
428     // we need two render frames here to clear the menu and cursor
429     // ... not sure why but doing an extra fgRenderFrame() shouldn't
430     // hurt anything
431     renderer->update( true );
432     renderer->update( true );
433
434     string dir = fgGetString("/sim/paths/screenshot-dir");
435     if (dir.empty())
436         dir = fgGetString("/sim/fg-current");
437
438     SGPath path(dir + '/');
439     if (path.create_dir( 0755 )) {
440         SG_LOG(SG_GENERAL, SG_ALERT, "Cannot create screenshot directory '"
441                 << dir << "'. Trying home directory.");
442         dir = fgGetString("/sim/fg-home");
443     }
444
445     char filename[24];
446     static int count = 1;
447     while (count < 1000) {
448         snprintf(filename, 24, "fgfs-screen-%03d.png", count++);
449
450         SGPath p(dir);
451         p.append(filename);
452         if (!p.exists()) {
453             path.set(p.str());
454             break;
455         }
456     }
457
458     int result = sg_glDumpWindow(path.c_str(),
459                                  fgGetInt("/sim/startup/xsize"),
460                                  fgGetInt("/sim/startup/ysize"));
461
462     fgSetString("/sim/paths/screenshot-last", path.c_str());
463     fgSetBool("/sim/signals/screenshot", false);
464
465     fgSetMouseCursor(mouse);
466
467     if ( !freeze ) {
468         fgSetBool("/sim/freeze/master", false);
469     }
470     return result != 0;
471 }
472
473 // do an entire scenegraph dump
474 void fgDumpSceneGraph()
475 {
476     char *filename = new char [24];
477     string message;
478     static int count = 1;
479
480     static const SGPropertyNode *master_freeze
481         = fgGetNode("/sim/freeze/master");
482
483     bool freeze = master_freeze->getBoolValue();
484     if ( !freeze ) {
485         fgSetBool("/sim/freeze/master", true);
486     }
487
488     while (count < 1000) {
489         FILE *fp;
490         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
491         if ( (fp = fopen(filename, "r")) == NULL )
492             break;
493         fclose(fp);
494     }
495
496     if ( fgDumpSceneGraphToFile(filename)) {
497         message = "Entire scene graph saved to \"";
498         message += filename;
499         message += "\".";
500     } else {
501         message = "Failed to save to \"";
502         message += filename;
503         message += "\".";
504     }
505
506     mkDialog (message.c_str());
507
508     delete [] filename;
509
510     if ( !freeze ) {
511         fgSetBool("/sim/freeze/master", false);
512     }
513 }
514
515     
516 // do an terrain branch dump
517 void fgDumpTerrainBranch()
518 {
519     char *filename = new char [24];
520     string message;
521     static int count = 1;
522
523     static const SGPropertyNode *master_freeze
524         = fgGetNode("/sim/freeze/master");
525
526     bool freeze = master_freeze->getBoolValue();
527     if ( !freeze ) {
528         fgSetBool("/sim/freeze/master", true);
529     }
530
531     while (count < 1000) {
532         FILE *fp;
533         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
534         if ( (fp = fopen(filename, "r")) == NULL )
535             break;
536         fclose(fp);
537     }
538
539     if ( fgDumpTerrainBranchToFile(filename)) {
540         message = "Terrain graph saved to \"";
541         message += filename;
542         message += "\".";
543     } else {
544         message = "Failed to save to \"";
545         message += filename;
546         message += "\".";
547     }
548
549     mkDialog (message.c_str());
550
551     delete [] filename;
552
553     if ( !freeze ) {
554         fgSetBool("/sim/freeze/master", false);
555     }
556 }
557
558 void fgPrintVisibleSceneInfoCommand()
559 {
560     static const SGPropertyNode *master_freeze
561         = fgGetNode("/sim/freeze/master");
562
563     bool freeze = master_freeze->getBoolValue();
564     if ( !freeze ) {
565         fgSetBool("/sim/freeze/master", true);
566     }
567
568     flightgear::printVisibleSceneInfo(globals->get_renderer());
569
570     if ( !freeze ) {
571         fgSetBool("/sim/freeze/master", false);
572     }
573 }
574
575     
576