skip process serial number argument on os X
[blender.git] / source / gameengine / GamePlayer / ghost / GPG_ghost.cpp
1 /**
2 * $Id$
3 *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31 * Start up of the Blender Player on GHOST.
32 */
33
34 #include <iostream>
35 #include <math.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifdef __linux__
42 #ifdef __alpha__
43 #include <signal.h>
44 #endif /* __alpha__ */
45 #endif /* __linux__ */
46
47 #ifdef __APPLE__
48 // Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
49 //#include <Carbon/Carbon.h>
50 //#include <CFBundle.h>
51 #endif // __APPLE__
52 #include "GEN_messaging.h"
53 #include "KX_KetsjiEngine.h"
54
55 /**********************************
56 * Begin Blender include block
57 **********************************/
58 #ifdef __cplusplus
59 extern "C"
60 {
61 #endif  // __cplusplus
62         
63 #include "BLI_blenlib.h"
64 #include "DNA_scene_types.h"
65 #include "BLO_readfile.h"
66 #include "BLO_readblenfile.h"
67         
68         int GHOST_HACK_getFirstFile(char buf[]);
69         
70 #ifdef __cplusplus
71 }
72 #endif // __cplusplus
73 /**********************************
74 * End Blender include block
75 **********************************/
76
77 #include "SYS_System.h"
78 #include "GPG_Application.h"
79 #include "GPC_PolygonMaterial.h"
80
81 #include "GHOST_ISystem.h"
82 #include "RAS_IRasterizer.h"
83
84 #include "BKE_main.h"
85 #include "BKE_utildefines.h"
86
87 #ifdef WIN32
88 #ifdef NDEBUG
89 #include <windows.h>
90 #include <wincon.h>
91 #endif // NDEBUG
92 #endif // WIN32
93
94 const int kMinWindowWidth = 100;
95 const int kMinWindowHeight = 100;
96
97 char bprogname[FILE_MAXDIR+FILE_MAXFILE];
98
99 void usage(char* program)
100 {
101         char * consoleoption;
102 #ifdef _WIN32
103         consoleoption = "-c ";
104 #else
105         consoleoption = "";
106 #endif
107         
108         printf("usage:   %s [-w [-p l t w h]] %s[-g gamengineoptions] "
109                 "[-s stereomode] filename.blend\n", program, consoleoption);
110         printf("  -h: Prints this command summary\n");
111         printf("  -w: display in a window\n");
112         printf("  -p: specify window position\n");
113         printf("       l = window left coordinate\n");
114         printf("       t = window top coordinate\n");
115         printf("       w = window width\n");
116         printf("       h = window height\n");
117         printf("  -f: start game in full screen mode\n");
118         printf("       fw = full screen mode pixel width\n");
119         printf("       fh = full screen mode pixel height\n");
120         printf("       fb = full screen mode bits per pixel\n");
121         printf("       ff = full screen mode frequency\n");
122         printf("  -s: start player in stereo\n");
123         printf("       stereomode: hwpageflip       (Quad buffered shutter glasses)\n");
124         printf("                   syncdoubling     (Above Below)\n");
125         printf("                   sidebyside       (Left Right)\n");
126         printf("                   anaglyph         (Red-Blue glasses)\n");
127         printf("                   vinterlace       (Vertical interlace for autostereo display)\n");
128         printf("                             depending on the type of stereo you want\n");
129 #ifdef _WIN32
130         printf("  -c: keep console window open\n");
131 #endif
132         printf("  -g: game engine options:\n");
133         printf("       Name            Default      Description\n");
134         printf("       ----------------------------------------\n");
135         printf("       fixedtime          0         Do the same timestep each frame \"Enable all frames\"\n");
136         printf("       nomipmap           0         Disable mipmaps\n");
137         printf("       show_framerate     0         Show the frame rate\n");
138         printf("       show_properties    0         Show debug properties\n");
139         printf("       show_profile       0         Show profiling information\n");
140         printf("       vertexarrays       1         Enable vertex arrays\n");
141         printf("\n");
142         printf("example: %s -p 10 10 320 200 -g noaudio c:\\loadtest.blend\n", program);
143         printf("example: %s -g vertexarrays = 0 c:\\loadtest.blend\n", program);
144 }
145
146 char *get_filename(int argc, char **argv) {
147 #ifdef __APPLE__
148 /* On Mac we park the game file (called game.blend) in the application bundle.
149 * The executable is located in the bundle as well.
150 * Therefore, we can locate the game relative to the executable.
151         */
152         int srclen = ::strlen(argv[0]);
153         int len = 0;
154         char *filename = NULL;
155         
156         if (argc > 1) {
157                 if (BLI_exists(argv[argc-1])) {
158                         len = ::strlen(argv[argc-1]);
159                         filename = new char [len + 1];
160                         ::strcpy(filename, argv[argc-1]);
161                         return(filename);
162                 }
163                 if (::strncmp(argv[argc-1], "-psn_", 5)==0) {
164                         static char firstfilebuf[512];
165                         if (GHOST_HACK_getFirstFile(firstfilebuf)) {
166                                 len = ::strlen(firstfilebuf);
167                                 filename = new char [len + 1];
168                                 ::strcpy(filename, firstfilebuf);
169                                 return(filename);
170                         }
171                 }                        
172         }
173         
174         srclen -= ::strlen("MacOS/blenderplayer");
175         if (srclen > 0) {
176                 len = srclen + ::strlen("Resources/game.blend"); 
177                 filename = new char [len + 1];
178                 ::strcpy(filename, argv[0]);
179                 ::strcpy(filename + srclen, "Resources/game.blend");
180                 //::printf("looking for file: %s\n", filename);
181                 
182                 if (BLI_exists(filename)) {
183                         return (filename);
184                 }
185         }
186         
187         return(NULL);
188 #else
189         return (argc>1)?argv[argc-1]:NULL;
190 #endif // !_APPLE
191 }
192
193 static BlendFileData *load_game_data(char *progname, char *filename = NULL) {
194         BlendReadError error;
195         BlendFileData *bfd = NULL;
196         
197         /* try to load ourself, will only work if we are a runtime */
198         if (blo_is_a_runtime(progname)) {
199                 bfd= blo_read_runtime(progname, &error);
200                 if (bfd) {
201                         bfd->type= BLENFILETYPE_RUNTIME;
202                         strcpy(bfd->main->name, progname);
203                 }
204         } else {
205                 bfd= BLO_read_from_file(progname, &error);
206         }
207         
208         /*
209         if (bfd && bfd->type == BLENFILETYPE_BLEND) {
210                 BLO_blendfiledata_free(bfd);
211                 bfd = NULL;
212                 error = BRE_NOT_A_PUBFILE;
213         }
214         */
215         
216         if (!bfd && filename) {
217                 bfd = load_game_data(filename);
218                 if (!bfd) {
219                         printf("Loading %s failed: %s\n", filename, BLO_bre_as_string(error));
220                 }
221         }
222         
223         return bfd;
224 }
225
226 int main(int argc, char** argv)
227 {
228         int i;
229         bool error = false;
230         SYS_SystemHandle syshandle = SYS_GetSystem();
231         bool fullScreen = false;
232         bool fullScreenParFound = false;
233         bool windowParFound = false;
234         bool closeConsole = true;
235         RAS_IRasterizer::StereoMode stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
236         bool stereoWindow = false;
237         bool stereoParFound = false;
238         int windowLeft = 100;
239         int windowTop = 100;
240         int windowWidth = 640;
241         int windowHeight = 480;
242         GHOST_TUns32 fullScreenWidth = 0;
243         GHOST_TUns32 fullScreenHeight= 0;
244         int fullScreenBpp = 32;
245         int fullScreenFrequency = 60;
246         
247 #ifdef __linux__
248 #ifdef __alpha__
249         signal (SIGFPE, SIG_IGN);
250 #endif /* __alpha__ */
251 #endif /* __linux__ */
252         BLI_where_am_i(bprogname, argv[0]);
253         
254 #ifdef __APPLE__
255     // Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
256     /*
257     IBNibRef            nibRef;
258     WindowRef           window;
259     OSStatus            err;
260         
261           // Create a Nib reference passing the name of the nib file (without the .nib extension)
262           // CreateNibReference only searches into the application bundle.
263           err = ::CreateNibReference(CFSTR("main"), &nibRef);
264           if (err) return -1;
265           
266                 // Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
267                 // object. This name is set in InterfaceBuilder when the nib is created.
268                 err = ::SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
269                 if (err) return -1;
270                 
271                   // We don't need the nib reference anymore.
272                   ::DisposeNibReference(nibRef);
273     */
274 #endif // __APPLE__
275         
276         GEN_init_messaging_system();
277  
278         // Parse command line options
279 #ifndef NDEBUG
280         printf("argv[0] = '%s'\n", argv[0]);
281 #endif
282
283         for (i = 1; (i < argc) && !error;)
284
285         {
286 #ifndef NDEBUG
287                 printf("argv[%d] = '%s'   , %i\n", i, argv[i],argc);
288 #endif
289                 if (argv[i][0] == '-')
290                 {
291                         switch (argv[i][1])
292                         {
293                         case 'g':
294                                 // Parse game options
295                                 {
296                                         i++;
297                                         if (i < argc)
298                                         {
299                                                 char* paramname = argv[i];
300                                                 // Check for single value versus assignment
301                                                 if (i+1 < argc && (*(argv[i+1]) == '='))
302                                                 {
303                                                         i++;
304                                                         if (i + 1 < argc)
305                                                         {
306                                                                 i++;
307                                                                 // Assignment
308                                                                 SYS_WriteCommandLineInt(syshandle, paramname, atoi(argv[i]));
309                                                                 SYS_WriteCommandLineFloat(syshandle, paramname, atof(argv[i]));
310                                                                 SYS_WriteCommandLineString(syshandle, paramname, argv[i]);
311 #ifndef NDEBUG
312                                                                 printf("%s = '%s'\n", paramname, argv[i]);
313 #endif
314                                                                 i++;
315                                                         }
316                                                         else
317                                                         {
318                                                                 error = true;
319                                                                 printf("error: argument assignment %s without value.\n", paramname);
320                                                         }
321                                                 }
322                                                 else
323                                                 {
324                                                         SYS_WriteCommandLineInt(syshandle, argv[i++], 1);
325                                                 }
326                                         }
327                                 }
328                                 break;
329                                 
330                         case 'p':
331                                 // Parse window position and size options
332                                 if (argv[i][2] == 0) {
333                                         i++;
334                                         if ((i + 4) < argc)
335                                         {
336                                                 windowLeft = atoi(argv[i++]);
337                                                 windowTop = atoi(argv[i++]);
338                                                 windowWidth = atoi(argv[i++]);
339                                                 windowHeight = atoi(argv[i++]);
340                                                 windowParFound = true;
341                                         }
342                                         else
343                                         {
344                                                 error = true;
345                                                 printf("error: too few options for window argument.\n");
346                                         }
347                                 } else { /* mac specific */
348                                 
349                     if (strncmp(argv[i], "-psn_", 5)==0) 
350                         i++; /* skip process serial number */
351                                 }
352                                 break;
353                         case 'f':
354                                 i++;
355                                 fullScreen = true;
356                                 fullScreenParFound = true;
357                                 if ((i + 2) < argc && argv[i][0] != '-' && argv[i+1][0] != '-')
358                                 {
359                                         fullScreenWidth = atoi(argv[i++]);
360                                         fullScreenHeight = atoi(argv[i++]);
361                                         if ((i + 1) < argc && argv[i][0] != '-')
362                                         {
363                                                 fullScreenBpp = atoi(argv[i++]);
364                                                 if ((i + 1) < argc && argv[i][0] != '-')
365                                                         fullScreenFrequency = atoi(argv[i++]);
366                                         }
367                                 }
368                                 break;
369                         case 'w':
370                                 // Parse window position and size options
371                                 {
372                                         fullScreen = false;
373                                         fullScreenParFound = true;
374                                         i++;
375                                 }
376                                 break;
377                         case 'h':
378                                 usage(argv[0]);
379                                 return 0;
380                                 break;
381                         case 'c':
382                                 i++;
383                                 closeConsole = false;
384                                 break;
385                         case 's':  // stereo
386                                 i++;
387                                 if ((i + 1) < argc)
388                                 {
389                                         stereomode = (RAS_IRasterizer::StereoMode) atoi(argv[i]);
390                                         if (stereomode < RAS_IRasterizer::RAS_STEREO_NOSTEREO || stereomode >= RAS_IRasterizer::RAS_STEREO_MAXSTEREO)
391                                                 stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
392                                         
393                                         if(!strcmp(argv[i], "nostereo"))  // ok, redundant but clear
394                                                 stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
395                                         
396                                         // only the hardware pageflip method needs a stereo window
397                                         if(!strcmp(argv[i], "hwpageflip")) {
398                                                 stereomode = RAS_IRasterizer::RAS_STEREO_QUADBUFFERED;
399                                                 stereoWindow = true;
400                                         }
401                                         if(!strcmp(argv[i], "syncdoubling"))
402                                                 stereomode = RAS_IRasterizer::RAS_STEREO_ABOVEBELOW;
403                                         
404                                         if(!strcmp(argv[i], "anaglyph"))
405                                                 stereomode = RAS_IRasterizer::RAS_STEREO_ANAGLYPH;
406                                         
407                                         if(!strcmp(argv[i], "sidebyside"))
408                                                 stereomode = RAS_IRasterizer::RAS_STEREO_SIDEBYSIDE;
409                                         
410                                         if(!strcmp(argv[i], "vinterlace"))
411                                                 stereomode = RAS_IRasterizer::RAS_STEREO_VINTERLACE;
412                                         
413 #if 0
414                                         // future stuff
415                                         if(strcmp(argv[i], "stencil")
416                                                 stereomode = RAS_STEREO_STENCIL;
417 #endif
418                                         
419                                         i++;
420                                         stereoParFound = true;
421                                 }
422                                 else
423                                 {
424                                         error = true;
425                                         printf("error: too few options for stereo argument.\n");
426                                 }
427                                 break;
428                         default:
429                                 printf("Unkown argument: %s\n", argv[i++]);
430                                 break;
431                         }
432                 }
433                 else
434                 {
435                         i++;
436                 }
437         }
438         
439         if ((windowWidth < kMinWindowWidth) || (windowHeight < kMinWindowHeight))
440         {
441                 error = true;
442                 printf("error: window size too small.\n");
443         }
444         
445         if (error)
446         {
447                 usage(argv[0]);
448         }
449         else
450         {
451 #ifdef __APPLE__
452                 //SYS_WriteCommandLineInt(syshandle, "show_framerate", 1);
453                 SYS_WriteCommandLineInt(syshandle, "nomipmap", 1);
454                 //fullScreen = false;           // Can't use full screen
455 #endif
456                 if (SYS_GetCommandLineInt(syshandle, "nomipmap", 0))
457                 {
458                         GPC_PolygonMaterial::SetMipMappingEnabled(0);
459                 }
460                 
461                 // Create the system
462                 if (GHOST_ISystem::createSystem() == GHOST_kSuccess)
463                 {
464                         GHOST_ISystem* system = GHOST_ISystem::getSystem();
465                         assertd(system);
466                         
467                         if (!fullScreenWidth || !fullScreenHeight)
468                                 system->getMainDisplayDimensions(fullScreenWidth, fullScreenHeight);
469                         // process first batch of events. If the user
470                         // drops a file on top off the blenderplayer icon, we 
471                         // recieve an event with the filename
472                         
473                         system->processEvents(0);
474                         
475                         // this bracket is needed for app (see below) to get out
476                         // of scope before GHOST_ISystem::disposeSystem() is called.
477                         {
478                                 int exitcode = KX_EXIT_REQUEST_NO_REQUEST;
479                                 STR_String exitstring = "";
480                                 GPG_Application app(system, NULL, exitstring);
481                                 bool firstTimeRunning = true;
482                                 
483                                 do
484                                 {
485                                         // Read the Blender file
486                                         char *filename = get_filename(argc, argv);
487                                         char *titlename;
488                                         char pathname[160];
489                                         BlendFileData *bfd;
490                                         
491                                         // if we got an exitcode 3 (KX_EXIT_REQUEST_START_OTHER_GAME) load a different file
492                                         if (exitcode == KX_EXIT_REQUEST_START_OTHER_GAME)
493                                         {
494                                                 char basedpath[160];
495                                                 
496                                                 // base the actuator filename with respect
497                                                 // to the original file working directory
498                                                 strcpy(basedpath, exitstring.Ptr());
499                                                 BLI_convertstringcode(basedpath, pathname, 0);
500                                                 
501                                                 bfd = load_game_data(basedpath);
502                                         }
503                                         else
504                                         {
505                                                 bfd = load_game_data(argv[0], filename);
506                                         }
507                                         
508                                         //::printf("game data loaded from %s\n", filename);
509                                         
510                                         if (!bfd) {
511                                                 usage(argv[0]);
512                                                 error = true;
513                                                 exitcode = KX_EXIT_REQUEST_QUIT_GAME;
514                                         } 
515                                         else 
516                                         {
517 #ifdef WIN32
518 #ifdef NDEBUG
519                                                 if (closeConsole)
520                                                 {
521                                                         ::FreeConsole();    // Close a console window
522                                                 }
523 #endif // NDEBUG
524 #endif // WIN32
525                                                 Main *maggie = bfd->main;
526                                                 Scene *scene = bfd->curscene;
527                                                 strcpy (pathname, maggie->name);
528                                                 char *startscenename = scene->id.name + 2;
529                                                 
530                                                 titlename = maggie->name;
531                                                 
532                                                 // Check whether the game should be displayed full-screen
533                                                 if ((!fullScreenParFound) && (!windowParFound))
534                                                 {
535                                                         // Only use file settings when command line did not override
536                                                         if (scene->r.fullscreen) {
537                                                                 //printf("fullscreen option found in Blender file\n");
538                                                                 fullScreen = true;
539                                                                 fullScreenWidth= scene->r.xplay;
540                                                                 fullScreenHeight= scene->r.yplay;
541                                                                 fullScreenFrequency= scene->r.freqplay;
542                                                                 fullScreenBpp = scene->r.depth;
543                                                         }
544                                                         else
545                                                         {
546                                                                 fullScreen = false;
547                                                                 windowWidth = scene->r.xplay;
548                                                                 windowHeight = scene->r.yplay;
549                                                         }
550                                                 }
551                                                 
552                                                 
553                                                 // Check whether the game should be displayed in stereo
554                                                 if (!stereoParFound)
555                                                 {
556                                                         stereomode = (RAS_IRasterizer::StereoMode) scene->r.stereomode;
557                                                         if (stereomode == RAS_IRasterizer::RAS_STEREO_QUADBUFFERED)
558                                                                 stereoWindow = true;
559                                                 }
560                                                 
561                                                 //                                      GPG_Application app (system, maggie, startscenename);
562                                                 app.SetGameEngineData(maggie, startscenename);
563                                                 
564                                                 if (firstTimeRunning)
565                                                 {
566                                                         firstTimeRunning = false;
567                                                         
568                                                         if (fullScreen)
569                                                         {
570                                                                 app.startFullScreen(fullScreenWidth, fullScreenHeight, fullScreenBpp, fullScreenFrequency,
571                                                                         stereoWindow, stereomode);
572                                                         }
573                                                         else
574                                                         {
575 #ifdef __APPLE__
576                                                                 // on Mac's we'll show the executable name instead of the 'game.blend' name
577                                                                 char tempname[1024], *appstring;
578                                                                 ::strcpy(tempname, titlename);
579                                                                 
580                                                                 appstring = strstr(tempname, ".app/");
581                                                                 if (appstring) {
582                                                                         appstring[2] = 0;
583                                                                         titlename = &tempname[0];
584                                                                 }
585 #endif
586                                                                 // Strip the path so that we have the name of the game file
587                                                                 STR_String path = titlename;
588 #ifndef WIN32
589                                                                 vector<STR_String> parts = path.Explode('/');
590 #else  // WIN32
591                                                                 vector<STR_String> parts = path.Explode('\\');
592 #endif // WIN32                        
593                                                                 STR_String title;
594                                                                 if (parts.size())
595                                                                 {
596                                                                         title = parts[parts.size()-1];
597                                                                         parts = title.Explode('.');
598                                                                         if (parts.size() > 1)
599                                                                         {
600                                                                                 title = parts[0];
601                                                                         }
602                                                                 }
603                                                                 else
604                                                                 {
605                                                                         title = "blenderplayer";
606                                                                 }
607                                                                 app.startWindow(title, windowLeft, windowTop, windowWidth, windowHeight,
608                                                                         stereoWindow, stereomode);
609                                                         }
610                                                 }
611                                                 else
612                                                 {
613                                                         app.StartGameEngine(stereomode);
614                                                         exitcode = KX_EXIT_REQUEST_NO_REQUEST;
615                                                 }
616                                                 
617                                                 // Add the application as event consumer
618                                                 system->addEventConsumer(&app);
619                                                 
620                                                 // Enter main loop
621                                                 bool run = true;
622                                                 while (run)
623                                                 {
624                                                         system->processEvents(false);
625                                                         system->dispatchEvents();
626                                                         if ((exitcode = app.getExitRequested()))
627                                                         {
628                                                                 run = false;
629                                                                 exitstring = app.getExitString();
630                                                         }
631                                                 }
632                                                 app.StopGameEngine();
633                                                 BLO_blendfiledata_free(bfd);
634                                                 
635 #ifdef __APPLE__
636                                                 if (filename) {
637                                                         delete [] filename;
638                                                 }
639 #endif // __APPLE__
640                                         }
641                                 } while (exitcode == KX_EXIT_REQUEST_RESTART_GAME || exitcode == KX_EXIT_REQUEST_START_OTHER_GAME);
642                         }
643                         // Dispose the system
644                         GHOST_ISystem::disposeSystem();
645                 } else {
646                         error = true;
647                         printf("error: couldn't create a system.\n");
648                 }
649         }
650         
651         return error ? -1 : 0;
652 }
653
654