1 /*
2  * stormbaancoureur
3  * (c) 2006,2007,2008 by Bram Stolk
4  * bram at gmail.com
5  * LICENSED ACCORDING TO THE GPLV3
6  */
7 
8 #include <assert.h>
9 #include <libgen.h>	// for dirname(), Is this portable?
10 
11 #define GL_GLEXT_PROTOTYPES 1
12 #define GLX_GLXEXT_PROTOTYPES 1
13 
14 #include <GL/glx.h>
15 #include <GL/glxext.h>	// for the opengl extensions we use for hardware accelerated shadowing
16 
17 #include <GL/gl.h>
18 #include <GL/glut.h>
19 
20 #include <plib/ssg.h>
21 #include <plib/ul.h>
22 
23 #include "ogl.h"
24 #include "staticworldobject.h"
25 #include "carobject.h"
26 #include "trackingcam.h"
27 #include "usercam.h"
28 #include "dynamicboxobject.h"
29 #include "dynamiccylobject.h"
30 
31 #include "stereocontext.h"
32 #include "intro.h"
33 #include "controller.h"
34 #include "soundenginealsa.h"
35 #include "plodegui.h"
36 #include "modelmap.h"
37 #include "postscore.h"
38 #include "sturmbahn.h"
39 #include "starsky.h"
40 
41 static float aspectratio=1.0;
42 static int winw=704, winh=300;
43 //static int winw=480, winh=360;
44 static ssgRoot *scene=0;
45 static ssgContext    *monocontext=0;
46 static StereoContext *stereocontext=0;
47 static ssgContext    *lightviewcontext=0;
48 static float fps=60.0;
49 static float gametime=0.0;
50 static float spawntime=0.0;
51 static const char *username="anonymou";
52 static float dt_hist[10]={0.016,0.016,0.016,0.016,0.016,0.016,0.016,0.016,0.016,0.016};
53 static std::string dirprefix;
54 
55 static dJointGroupID contactgroup;
56 static dSpaceID bigspace;
57 static dSpaceID staticspace;
58 static dWorldID world;
59 
60 static SturmBahnFull     *sturmbahnfull=0;
61 static SturmBahnPractice *sturmbahnpractice=0;
62 static SturmBahnRally    *sturmbahnrally=0;
63 static RespawnPoint      *respawn_point_hit=0;
64 
65 static ControllerPad *controller_pad=0;
66 static ControllerKey *controller_key=0;
67 static Controller    *controller=0;
68 static CarObject *car=0;
69 static UserCam *trackingcam=0;
70 static int numcars=0;
71 
72 static SoundEngineAlsa *sengine=0;
73 static Intro *intro=0;
74 static PlodeGUI *gui=0;
75 static bool capture=false;
76 
77 static ModelMap *modelmap=0;
78 static StarSky  *starsky=0;
79 
80 static bool do_shadows;
81 
82 static void stop_game(void);
83 static void start_game(const std::string &gametype);
84 static void restart_game(void);
85 static bool respawn(void);
86 
87 
88 
near_callback(void * data,dGeomID o1,dGeomID o2)89 static void near_callback(void *data, dGeomID o1, dGeomID o2)
90 {
91   assert(o1);
92   assert(o2);
93 
94   if (dGeomIsSpace(o1) || dGeomIsSpace(o2))
95   {
96     // colliding a space with something
97     dSpaceCollide2(o1,o2,data,&near_callback);
98     // Note we do not want to test intersections within a space,
99     // only between spaces.
100     return;
101   }
102 
103   // Two non space geoms
104 
105   WorldObject *wo1 = static_cast<WorldObject*>(dGeomGetData(o1));
106   WorldObject *wo2 = static_cast<WorldObject*>(dGeomGetData(o2));
107 
108   const int N = 32;
109   dContact contact[N];
110   int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact));
111 
112   if (n > 0)
113   {
114     assert(wo1);
115     assert(wo2);
116 
117     if (wo1->name == "respawnpoint")
118       respawn_point_hit = dynamic_cast<RespawnPoint*>(wo1);
119     if (wo2->name == "respawnpoint")
120       respawn_point_hit = dynamic_cast<RespawnPoint*>(wo2);
121 
122     // This section is req'd to see if the backwheels are touching
123     // a dynamic object. If so, it could be that our car is riding
124     // on top of an object. In this case, it should be possible to
125     // engage the reverse-gear when the car is stationary relative
126     // to the ground, and yet may be moving in absolute space.
127     if (car->IsBackWheelGeometry(o1))
128     {
129       DynamicObject *dynamic_ground = dynamic_cast<DynamicObject*>(wo2);
130       if (dynamic_ground)
131         car->SetGround(dynamic_ground);
132       else
133         car->SetGround(0);
134     }
135     if (car->IsBackWheelGeometry(o2))
136     {
137       DynamicObject *dynamic_ground = dynamic_cast<DynamicObject*>(wo1);
138       if (dynamic_ground)
139         car->SetGround(dynamic_ground);
140       else
141         car->SetGround(0);
142     }
143 
144     bool is_tyre=false;
145     sgVec3 wheel_dir={0,0,0};
146     float wheel_vel=0.0f;
147     if (car->IsWheelGeometry(o1,wheel_vel))
148     {
149       const dReal *m = dGeomGetRotation(o1);
150       sgSetVec3(wheel_dir, m[2],m[6],m[10]);
151       is_tyre=true;
152     }
153     if (car->IsWheelGeometry(o2,wheel_vel))
154     {
155       const dReal *m = dGeomGetRotation(o2);
156       sgSetVec3(wheel_dir, m[2],m[6],m[10]);
157       is_tyre=true;
158     }
159 
160 #if 0
161     if (is_tyre)
162       fprintf(stderr,"dir %5.2f %5.2f %5.2f\n", wheel_dir[0], wheel_dir[1], wheel_dir[2]);
163 #endif
164 
165     // Create constraints for all the contacts between the geometries.
166     for (int i=0; i<n; i++)
167     {
168       contact[i].surface.mu = 50.0;
169       //contact[i].surface.soft_erp = 0.985;
170       //contact[i].surface.soft_cfm = 0.020;
171       //contact[i].surface.mode = dContactSoftERP | dContactSoftCFM | dContactApprox1;
172       contact[i].surface.mode = dContactApprox1;
173 
174       if (is_tyre)
175       {
176         contact[i].fdir1[0]=wheel_dir[0];
177         contact[i].fdir1[1]=wheel_dir[1];
178         contact[i].fdir1[2]=wheel_dir[2];
179         contact[i].surface.slip1 = 0.0250f * wheel_vel; // slip perpendicular to roll direction
180         contact[i].surface.slip2 = 0.0005f * wheel_vel; // slip in roll direction
181         contact[i].surface.mode = contact[i].surface.mode | dContactFDir1 | dContactSlip1 | dContactSlip2;
182 #if 0
183         contact[i].surface.soft_erp = 0.95;
184         contact[i].surface.soft_cfm = 0.05;
185         contact[i].surface.mode = contact[i].surface.mode | dContactSoftERP | dContactSoftCMF;
186 #endif
187         contact[i].surface.mu  = 340.0;
188         contact[i].surface.mu2 = 340.0;
189       }
190 
191       if ((wo1->name == "ferriswheelcart" || wo2->name == "ferriswheelcart") && is_tyre)
192       {
193         contact[i].surface.mu  *= 2;
194         contact[i].surface.mu2 *= 2;
195         contact[i].surface.slip1 *= 0.01;
196         contact[i].surface.slip2 *= 0.01;
197       }
198 
199       dJointID c = dJointCreateContact (world,contactgroup,&contact[i]);
200       dGeomID g1 = contact[i].geom.g1;
201       dGeomID g2 = contact[i].geom.g2;
202       dBodyID b1 = dGeomGetBody(g1);
203       dBodyID b2 = dGeomGetBody(g2);
204       assert(b1 || b2);
205       dJointAttach (c, b1, b2);
206     }
207   }
208 }
209 
210 
engine_noise(float dt)211 static void engine_noise(float dt)
212 {
213   float vl = car->GetWheelVelocity(2);
214   float vr = car->GetWheelVelocity(3);
215   float v = std::max(vl,vr);
216   float a = car->GetAccelerator();
217   float d = car->GetBrake();
218   float e = std::max(a,d);
219   v = v/15.0;
220   if (v>3) v=3;
221   sengine->SetModulation(1+v+e, 1+v/5+e/2);
222   sengine->SetLowPass(0.97);
223   return;
224 }
225 
226 
make_menu_choice(void)227 void make_menu_choice(void)
228 {
229   const std::string &choice = gui->SelectChoice();
230   if (choice == "ATTACK STORMBAAN")
231     start_game("full");
232   if (choice == "PRACTICE")
233     start_game("practice");
234   if (choice == "TEST GROUND")
235     start_game("rally");
236 }
237 
238 
idle(void)239 static void idle(void)
240 {
241   static ulClock clk;
242   static int frameCounter = 0;
243   clk.setMaxDelta(1.0);
244 
245   float elapsed = clk.getDeltaTime();
246   clk.update();
247 
248   float fractiondone=0;
249   if (sengine)
250     fractiondone=sengine->Sustain();
251   if (intro)
252   {
253     bool done=intro->Sustain(elapsed, fractiondone);
254     if (done || !gui->hasmenu)
255     {
256       delete intro;
257       intro = 0;
258     }
259   }
260 
261   dt_hist[frameCounter] = elapsed;
262   frameCounter = (frameCounter+1)%10;
263 
264   float dt=0;
265   int i;
266   for (i=0; i<10; i++)
267     dt += dt_hist[i];
268   dt = dt / 10;
269   assert(dt>0.0);
270   // do not update the fps every time, it is not readable when it is done each tick
271   if (frameCounter == 0)
272     fps = 1.0f / dt;
273   if (dt > 0.125) dt=0.125; // Do not accomodate systems slower than 8 fps
274 
275   if (controller_pad) controller_pad->Sustain(dt);
276   if (controller_key) controller_key->Sustain(dt);
277 
278   // Feed the controls into the car
279   float ste = controller->GetSteer();
280   float acc = controller->GetAccel();
281   float bra = controller->GetBrake();
282   bool  act = controller->GetAction();
283   bool  ebr = controller->GetEBrake();
284 
285   // Steer the gui with the gamepad
286   if (gui && gui->hasmenu && controller_pad)
287   {
288     if (controller_pad->HasAnyButtonChanged(true))
289       make_menu_choice();
290     static float delta=0.0;
291     delta += controller_pad->GetAxisValue(1)*10*dt;
292     int idelta = int(delta);
293     if (idelta)
294     {
295       delta=0.0;
296       gui->ChangeChoice(idelta);
297     }
298   }
299 
300   if (intro)
301     return;
302 
303   if (gui && gui->hasmenu)
304     return;
305 
306   car->SetSteer(ste);
307   car->SetAccelerator(acc);
308   car->SetBrake(bra);
309   car->SetAction(act);
310   car->SetEBrake(ebr);
311 
312   gametime += elapsed;
313   spawntime += elapsed;
314   if (sengine)
315     engine_noise(dt);
316 
317   float timestep=0.005555; // Use 5.555ms timesteps
318   static float remaining_sim_time=0;
319   remaining_sim_time += dt;
320 
321   bool win=false;
322   const float SLOWMOTION=1.0f;
323   while (remaining_sim_time > timestep)
324   {
325     dSpaceCollide (bigspace, 0, &near_callback);
326     if (car) car->Sustain(SLOWMOTION*timestep);
327     if (sturmbahnpractice)
328       sturmbahnpractice->Sustain(SLOWMOTION*timestep, car->GetAction());
329     if (sturmbahnfull)
330       if (sturmbahnfull->Sustain(SLOWMOTION*timestep, car->GetAction()))
331         win=true;
332     if (sturmbahnrally)
333       sturmbahnrally->Sustain(SLOWMOTION*timestep, car->GetAction());
334     dWorldQuickStep (world, SLOWMOTION*timestep);
335     dJointGroupEmpty (contactgroup);
336     remaining_sim_time -= timestep;
337   }
338 
339   if (win)
340   {
341     postscore_put(username, gametime);
342     const char *leaderboardtext = postscore_get();
343     gui->SetLeaderBoardText(leaderboardtext);
344     stop_game();
345     return;
346   }
347 
348   // Check lose condition
349   sgVec3 carpos;
350   car->GetPos(carpos);
351   if (carpos[2] < -4)
352   {
353     restart_game();
354     return;
355   }
356 
357   if (respawn_point_hit)
358   {
359     car->SnapShot(false);
360     gametime += 60.0f; // penalty for taking the easy way out
361     respawn_point_hit->Disable();
362     respawn_point_hit=0;
363   }
364 
365   trackingcam->Update(dt);
366   sgVec3 eye, coi, up;
367   sgSetVec3(up,0,0,1);
368   trackingcam->GetCameraPos(eye);
369   trackingcam->GetTargetPos(coi);
370 
371   if (monocontext)
372     monocontext->setCameraLookAt(eye, coi, up);
373 
374   if (stereocontext)
375   {
376     stereocontext->SetCameraLookAt(eye, coi, up);
377     OglErrorCheck("stereocontext->SetCameraLookAt");
378   }
379 
380   ssgLight *light = ssgGetLight(0);
381   if (do_shadows)
382   {
383     eye[0]=  0.0;
384     eye[1]= 40.0;
385     eye[2]= 90.0;
386   }
387   else
388   {
389     car->GetPos(coi);
390     car->GetPos(eye);
391     eye[2] += 6.5;
392   }
393 
394   sgVec3 car_dir;
395   car->GetDir(car_dir);
396   sgAddVec3(coi, coi, car_dir);
397   sgVec3 light_dir;
398   sgSubVec3(light_dir, coi, eye);
399   light->setPosition(eye[0],eye[1],eye[2]);
400   light->setSpotDirection(light_dir[0],light_dir[1],light_dir[2]);
401   sgSetVec3(up,1,0,0);
402   lightviewcontext->setCameraLookAt(eye, coi, up);
403 }
404 
405 
redraw_single_channel(void)406 static void redraw_single_channel(void)
407 {
408   if (scene && !intro && (!gui || !gui->hasmenu))
409   {
410     if (do_shadows)
411     {
412       // 1st pass from light view
413 
414       lightviewcontext->makeCurrent();
415       OglRenderShadowMap(scene);
416 
417       sgMat4 mat_cam, mat_spt;
418       if (monocontext)
419       {
420         monocontext->makeCurrent();
421         monocontext->getModelviewMatrix(mat_cam);
422       }
423       if (stereocontext)
424       {
425         stereocontext->MakeCurrent("last");
426         stereocontext->getModelviewMatrix(mat_cam);
427       }
428       lightviewcontext->getModelviewMatrix(mat_spt);
429 
430       OglSetupSecondPass((float*)mat_cam, (float*)mat_spt);
431     }
432 
433     ssgCullAndDraw(scene);
434     OglErrorCheck("ssgCullAndDraw");
435 
436     if (do_shadows)
437     {
438       glUseProgram(0);
439       // Turn off all texture units
440       glActiveTextureARB(GL_TEXTURE1_ARB);
441       glBindTexture(GL_TEXTURE_2D, 0);
442       glDisable(GL_TEXTURE_2D);
443       glActiveTextureARB(GL_TEXTURE2_ARB);
444       glBindTexture(GL_TEXTURE_2D, 0);
445       glDisable(GL_TEXTURE_2D);
446       glActiveTextureARB(GL_TEXTURE0_ARB);
447       glBindTexture(GL_TEXTURE_2D, 0);
448       glDisable(GL_TEXTURE_2D);
449       OglErrorCheck("Turn off texture units");
450     }
451   }
452 
453   if (intro)
454   {
455     if (do_shadows)
456     {
457       glUseProgram(0);
458       glDisable(GL_TEXTURE_2D);
459     }
460     intro->Redraw(winw, winh);
461     OglErrorCheck("intro->Redraw");
462   }
463 
464   if (gui)
465   {
466     char s[25];
467     sprintf(s, "%.02f   %d FPS", gametime, (int) fps);
468     gui->Redraw(winw, winh, s);
469     OglErrorCheck("gui->Redraw");
470   }
471 }
472 
473 
redraw(void)474 static void redraw(void)
475 {
476   if (intro || (gui && gui->hasmenu))
477     glClearColor(0.3,0.3,0.55,1);
478   else
479     glClearColor(0.2,0.1,0.35,1);
480 
481   if (stereocontext)
482   {
483     // passive stereo: clear buffers only once
484     if (!stereocontext->IsQuadBuffered())
485       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
486     stereocontext->MakeCurrent("left");
487     OglErrorCheck("MakeCurrent");
488 #if 1
489     // quad buffer stereo: clear buffers twice
490     if (stereocontext->IsQuadBuffered())
491       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
492 #endif
493     redraw_single_channel();
494     stereocontext->MakeCurrent("right");
495     OglErrorCheck("MakeCurrent");
496 #if 1
497     // quad buffer stereo: clear buffers twice
498     if (stereocontext->IsQuadBuffered())
499       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
500 #endif
501     redraw_single_channel();
502   }
503 
504   if (monocontext)
505   {
506     monocontext->makeCurrent();
507     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
508     redraw_single_channel();
509   }
510 
511   glutSwapBuffers () ;
512   OglErrorCheck("glutSwapBuffers");
513 
514   if (capture)
515   {
516     static char *buf = new char[winw * winh * 3];
517     glMatrixMode(GL_MODELVIEW);
518     glLoadIdentity();
519     glReadBuffer(GL_BACK_LEFT);
520     glReadPixels(0,0,winw,winh,GL_RGB,GL_UNSIGNED_BYTE,(GLvoid*)(buf));
521     OglErrorCheck("glReadPixels");
522     static FILE *f=0;
523 
524     if (!f)
525     {
526       f=fopen("capture.out", "wb");
527       assert(f);
528  //        fprintf(f,"P6 %d %d 255\n", winw,winh);
529     }
530     unsigned char *revbuf = new unsigned char[winw*winh*3];
531     for (int y=0; y<winh; y++)
532     {
533       int chunk = 3 * winw;
534       memcpy(revbuf + y*chunk, buf + (winh - 1 - y)*chunk, chunk);
535     }
536     fwrite(revbuf,winw*winh*3,1,f);
537     fflush(f);
538     delete revbuf;
539   }
540   // We want to render the next frame immediately
541   glutPostRedisplay () ;
542 }
543 
544 
reshape(int w,int h)545 static void reshape(int w, int h)
546 {
547   winw=w; winh=h;
548   if (monocontext)
549   {
550     glViewport ( 0, 0, w, h ) ;
551     aspectratio = w / (float) h;
552     float fovy = 40.0 * M_PI / 180.0;
553     float nearPlaneDistance = 0.28;
554     float farPlaneDistance  = 2500.0;
555     float y = tan(0.5 * fovy) * nearPlaneDistance;
556     float x = aspectratio * y;
557     monocontext->setFrustum(-x,x,-y,y,nearPlaneDistance,farPlaneDistance);
558   }
559 
560   if (stereocontext)
561     stereocontext->SetWindowSize(winw, winh);
562 
563 #if 1
564   int nsamples = glutGet(GLUT_WINDOW_NUM_SAMPLES);
565 #else
566   int nsamples;
567   glGetIntegerv(GL_SAMPLES, &nsamples);
568 #endif
569   fprintf(stderr,"Number of samples per pixel: %d\n", nsamples);
570 }
571 
572 
keyboardUp(unsigned char k,int,int)573 static void keyboardUp(unsigned char k, int, int)
574 {
575   if (controller_key) controller_key->FeedKey(k, false);
576 }
577 
578 
special(int k,int,int)579 static void special(int k, int, int)
580 {
581   if (k == GLUT_KEY_UP)
582   {
583     if (gui && gui->hasmenu) gui->ChangeChoice(-1);
584     if (controller_key) controller_key->FeedAction("ACCEL", true);
585   }
586   if (k == GLUT_KEY_DOWN)
587   {
588     if (gui && gui->hasmenu) gui->ChangeChoice(1);
589     if (controller_key) controller_key->FeedAction("BRAKE", true);
590   }
591   if (k == GLUT_KEY_LEFT)
592   {
593     if (controller_key) controller_key->FeedAction("LEFT", true);
594   }
595   if (k == GLUT_KEY_RIGHT)
596   {
597      if (controller_key) controller_key->FeedAction("RIGHT", true);
598   }
599 }
600 
601 
specialUp(int k,int,int)602 static void specialUp(int k, int, int)
603 {
604   if (k == GLUT_KEY_UP)
605   {
606      if (controller_key) controller_key->FeedAction("ACCEL", false);
607   }
608   if (k == GLUT_KEY_DOWN)
609   {
610     if (controller_key) controller_key->FeedAction("BRAKE", false);
611   }
612   if (k == GLUT_KEY_LEFT)
613   {
614     if (controller_key) controller_key->FeedAction("LEFT", false);
615   }
616   if (k == GLUT_KEY_RIGHT)
617   {
618     if (controller_key) controller_key->FeedAction("RIGHT", false);
619   }
620 }
621 
622 
623 static int mouse_x, mouse_y, mouse_but;
mouse(int button,int state,int x,int y)624 static void mouse(int button, int state, int x, int y)
625 {
626   mouse_x = x;
627   mouse_y = y;
628   if (state == GLUT_DOWN) mouse_but = button;
629 }
630 
631 
motion(int x,int y)632 static void motion(int x, int y)
633 {
634   if (!trackingcam) return;
635   float dx = (x - mouse_x) / (float) winw;
636   float dy = (y - mouse_y) / (float) winh;
637 
638   if (mouse_but == GLUT_LEFT_BUTTON)
639   {
640     trackingcam->ChangeHeading(100.0 * dx);
641     trackingcam->ChangePitch(100.0 * dy);
642   }
643   if (mouse_but == GLUT_MIDDLE_BUTTON)
644   {
645     trackingcam->ChangeOffset(dx, dy);
646   }
647   if (mouse_but == GLUT_RIGHT_BUTTON)
648   {
649     trackingcam->ChangeDistance(-10.0 * dy);
650   }
651 
652   mouse_x = x;
653   mouse_y = y;
654 }
655 
656 
start_game(const std::string & gametype)657 static void start_game(const std::string &gametype)
658 {
659   gametime = 0.0;
660   spawntime = 0.0;
661 
662   // Create ODE world
663   dInitODE();
664   world = dWorldCreate();
665   bigspace = dHashSpaceCreate(0);
666   staticspace = dSimpleSpaceCreate(bigspace);
667   contactgroup = dJointGroupCreate (0);
668 #if 0
669   dWorldSetERP(world, 0.95f);
670   dWorldSetCFM(world, 0.001f);
671 #endif
672   dWorldSetGravity(world,0,0,-9.8);
673   dWorldSetAutoDisableFlag(world, true);
674   dWorldSetAutoDisableLinearThreshold(world, 0.04);
675   dWorldSetAutoDisableAngularThreshold(world, 0.04);
676   dWorldSetQuickStepNumIterations(world, 16);
677 
678   // Create PLIB world
679 
680   scene = new ssgRoot();
681 
682   if (gametype=="practice")
683     sturmbahnpractice = new SturmBahnPractice(world, bigspace, staticspace, scene, modelmap, dirprefix);
684   if (gametype=="rally")
685     sturmbahnrally = new SturmBahnRally(world, bigspace, staticspace, scene, modelmap, dirprefix);
686   if (gametype=="full")
687     sturmbahnfull = new SturmBahnFull(world, bigspace, staticspace, scene, modelmap, dirprefix);
688 
689   // Create a car
690   sgVec3 carpos;
691 
692   if (sturmbahnpractice)
693     sgSetVec3(carpos, -4, 0, 1.5);
694 
695   if (sturmbahnfull)
696     sgSetVec3(carpos, -17, 0, 3.0);
697     //sgSetVec3(carpos, 52, 0, 1.5);  // If you wanna cheat!
698 
699   if (sturmbahnrally)
700     sgSetVec3(carpos, -28, 0, 1.5);
701 
702   car = new CarObject
703   (
704     modelmap->Get("frame.3ds"),
705     modelmap->Get("fivespoke.3ds"),
706     modelmap->Get("wishbone.3ds"),
707     modelmap->Get("spindle.3ds"),
708     modelmap->Get("coilspring.3ds"),
709     modelmap->Get("leafspring.3ds"),
710     modelmap->Get("rearaxle.3ds"),
711     world,
712     bigspace,
713     carpos
714   );
715   scene->addKid(car->GetEntity());
716   numcars = 1;
717 
718   // Setup camera
719   trackingcam = new UserCam(0,0,0);
720   trackingcam->AddTarget(car->GetTransform());
721   trackingcam->SetPitch(16.5f);
722   trackingcam->SetDistance(4.0f);
723 
724   if (sengine)
725   {
726     sengine->SetMode("engine");
727     sengine->Play(dirprefix + "/sounds/detonationnorm_s16_le.wav",0);
728   }
729 }
730 
731 
stop_game(void)732 static void stop_game(void)
733 {
734   delete car;
735   delete trackingcam;
736 
737   if (sengine)
738     sengine->SetMode("none");
739 
740   delete sturmbahnfull;
741   sturmbahnfull=0;
742   delete sturmbahnpractice;
743   sturmbahnpractice=0;
744   delete sturmbahnrally;
745   sturmbahnrally=0;
746 
747   // Delete ODE stuff
748   dJointGroupDestroy(contactgroup);
749   dSpaceDestroy(staticspace);
750   dSpaceDestroy(bigspace);
751   dWorldDestroy(world);
752 
753   dCloseODE();
754 }
755 
756 
respawn(void)757 static bool respawn(void)
758 {
759   spawntime=0.0f;
760   bool restored = car->SnapShot(true);
761   if (!restored)
762     return false;
763   return true;
764 }
765 
766 
restart_game(void)767 static void restart_game(void)
768 {
769   const char *gametype="";
770   if (sturmbahnpractice) gametype="practice";
771   if (sturmbahnrally) gametype="rally";
772   if (sturmbahnfull) gametype="full";
773   bool respawned = respawn();
774   if (!respawned)
775   {
776     stop_game();
777     start_game(gametype);
778   }
779 }
780 
781 
keyboard(unsigned char k,int,int)782 static void keyboard(unsigned char k, int, int)
783 {
784   if (k==27)
785   {
786     if (gui && gui->hasmenu)
787     {
788       if (gui->showingleaders)
789       {
790         gui->showingleaders = false;
791         return;
792       }
793       if (gui->showinghelp)
794       {
795         gui->showinghelp = false;
796         return;
797       }
798       exit(0);
799     }
800     if (spawntime>1.0f)
801       restart_game();
802     else
803       if (gui) gui->hasmenu=true;
804   }
805 
806   if (gui && gui->hasmenu)
807   {
808     if (k==' ' || k==13 || k==10)
809     {
810       make_menu_choice();
811     }
812     return;
813   }
814 
815 #if 0
816   if (k=='x')
817   {
818     capture=!capture;
819     return;
820   }
821 #endif
822 
823   if (controller_key) controller_key->FeedKey(k, true);
824 }
825 
826 
827 
828 
829 
main(int argc,char * argv[])830 int main(int argc, char *argv[])
831 {
832   std::string displaymode="monoscopic";
833 
834   fprintf(stderr,"Version %s\n", VERSION_STRING(GAMEVERSION));
835   fprintf(stderr,"Stormbaan Coureur is (c)2006-2008 by Bram Stolk\n");
836   fprintf(stderr,"plib is (c) by Steve Baker\n");
837   fprintf(stderr,"OpenDE is (c) by Russel L. Smith\n");
838 
839   char *bindirname  = dirname(argv[0]);
840   dirprefix="/usr/local/share/stormbaancoureur";
841   if (getenv("PLODE_DATADIR"))
842     dirprefix = getenv("PLODE_DATADIR");
843   if (getenv("PLODE_DISPLAYMODE"))
844     displaymode = getenv("PLODE_DISPLAYMODE");
845   assert(displaymode == "monoscopic" || displaymode == "quadbufferstereoscopic" || displaymode == "passivestereoscopic");
846   modelmap = new ModelMap(dirprefix);
847   char *unam = getenv("USER");
848   if (unam && strlen(unam)>2)
849   {
850     username = unam;
851     if (strlen(unam)>8)
852       unam[8]=0;
853   }
854   glutInit(&argc, argv);
855   int flags = GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE;
856   if (displaymode == "quadbufferstereoscopic") flags = flags | GLUT_STEREO;
857   glutInitDisplayMode(flags);
858   glutInitWindowSize(winw, winh);
859   glutInitWindowPosition(0,0);
860   int winid=glutCreateWindow ("Stormbaan Coureur");
861   assert(winid>=1);
862   glutDisplayFunc(redraw);
863   glutReshapeFunc(reshape);
864   glutKeyboardFunc(keyboard);
865   glutKeyboardUpFunc(keyboardUp);
866   glutSpecialFunc(special);
867   glutSpecialUpFunc(specialUp);
868   glutMouseFunc(mouse);
869   glutMotionFunc(motion);
870   glutIdleFunc(idle);
871 
872 #if 1
873   glutFullScreen();
874 #else
875   if (1)
876   {
877     int width = glutGameModeGet( GLUT_GAME_MODE_WIDTH );
878     int height = glutGameModeGet( GLUT_GAME_MODE_HEIGHT );
879     int pixelDepth = glutGameModeGet( GLUT_GAME_MODE_PIXEL_DEPTH );
880     int refreshRate = glutGameModeGet( GLUT_GAME_MODE_REFRESH_RATE );
881     fprintf(stderr,"%dx%d-%d@%d\n", width, height, pixelDepth, refreshRate);
882     //glutEnterGameMode();
883   }
884 #endif
885 
886   do_shadows = OglCanDoShadowing();
887   fprintf
888   (
889     stderr,
890     "This platform %s all required GL extensions to do hardware accelerated shadowing.\n",
891     (do_shadows) ? "supports" : "does not support"
892   );
893 
894   ssgInit();
895 
896   if (do_shadows)
897   {
898     OglInitShadowing(dirprefix);
899   }
900 
901   glEnable(GL_DEPTH_TEST);
902   glEnable(GL_CULL_FACE);
903 
904   float amb[4]={0.0, 0.0, 0.0, 1};
905   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb);
906 
907   starsky = new StarSky();
908   modelmap->Put("starsky", starsky->GetEntity());
909 
910   controller_pad = new ControllerPad("/dev/input/js0");
911   if (controller_pad->IsWorking())
912   {
913     fprintf(stderr,"Using gamepad\n");
914     controller = controller_pad;
915   }
916   else
917   {
918     delete controller_pad;
919     controller_pad = 0;
920     char *ev = getenv("HOME");
921     controller_key = new ControllerKey(std::string((ev)?ev:"~") + "/.stormbaancoureur.keys");
922     controller = controller_key;
923     fprintf(stderr,"Using keyboard\n");
924   }
925 
926 
927   if (displaymode != "monoscopic")
928   {
929     stereocontext = new StereoContext(displaymode == "quadbufferstereoscopic");
930   }
931   else
932   {
933     sgVec3 eye={5.2,1.8,3.4};
934     sgVec3 coi={0,0,0};
935     sgVec3 up= {0,0,1};
936     monocontext = new ssgContext();
937     monocontext->setCameraLookAt(eye, coi, up);
938     monocontext->makeCurrent();
939   }
940   lightviewcontext = new ssgContext();
941   float fov = spot_fov * M_PI / 180.0;
942   float nearPlaneDistance = 1.0f;
943   float farPlaneDistance  = 150.0f;
944   float s = tan(0.5 * fov) * nearPlaneDistance;
945   lightviewcontext->setFrustum(-s,s,-s,s, nearPlaneDistance, farPlaneDistance);
946 
947   ssgLight *light=ssgGetLight(0);
948   const float ka = (do_shadows) ? 0.15f : 0.30f;
949   const float kd = (do_shadows) ? 1.00f : 2.50f;
950   const float ks = 1.0f;
951   light->setColour(GL_DIFFUSE,  kd,kd,kd);
952   light->setColour(GL_AMBIENT,  ka,ka,ka);
953   light->setColour(GL_SPECULAR, ks,ks,ks);
954   light->setPosition(0,0,8);
955   light->setSpotlight(true);
956   light->setSpotDirection(0,0,-1);
957   light->setSpotDiffusion(100,180);
958   light->setSpotAttenuation(1, 0.1, 0.04);
959   light->on();
960 
961   sgCoord campos ;
962   sgSetCoord (&campos, 0.0f, -13.0f,  6.0f, 0.0, -34.0f, 0.0f) ;
963 
964   bool nosound = getenv("PLODE_NO_SOUND");
965   if (!nosound)
966   {
967     sengine = new SoundEngineAlsa(5000);
968     if (!sengine->opened)
969     {
970       delete sengine;
971       sengine = 0;
972     }
973   }
974   if (sengine)
975     intro = new Intro(sengine, dirprefix, modelmap->Get("piston.3ds"), modelmap->Get("rod.3ds"));
976   gui = new PlodeGUI(username, controller_pad!=0);
977 
978   glutMainLoop();
979   delete sengine;
980 }
981 
982