3 #include <SDL_opengl.h>
13 void viewport(Display
* display
, GLsizei w
, GLsizei h
, GLsizei bpp
,
14 bool fullscreen
, int aa
=0)
17 SDL_GL_SetAttribute( SDL_GL_RED_SIZE
, bpp
/3 );
18 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE
, bpp
/3 );
19 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE
, bpp
/3 );
20 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE
, 16 );
21 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER
, 1 );
24 SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS
, 1 );
25 SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES
, aa
);
28 int flags
= SDL_HWSURFACE
|SDL_OPENGLBLIT
|SDL_RESIZABLE
;
30 flags
|= SDL_FULLSCREEN
;
31 display
->screen
= SDL_SetVideoMode(w
, h
, bpp
, flags
);
32 if(display
->screen
== NULL
)
35 bpp
= display
->screen
->format
->BitsPerPixel
;
36 //printf("%dx%dx%d\n", display->screen->w, display->screen->h, display->screen->format->BitsPerPixel);
41 SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS
, &arg
);
42 printf("SDL_GL_MULTISAMPLEBUFFERS %d\n", arg
);
43 SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES
, &arg
);
44 printf("SDL_GL_MULTISAMPLESAMPLES %d\n", arg
);
48 SDL_WM_SetCaption("cave9 -- 9hells.org", "cave9");
49 SDL_ShowCursor(SDL_DISABLE
);
54 glMatrixMode(GL_PROJECTION
);
56 gluPerspective(45,-w
/(GLfloat
)h
,display
->near_plane
,display
->far_plane
);
60 glClearColor(0,0,0,0);
63 glDepthFunc(GL_LEQUAL
);
64 glEnable(GL_DEPTH_TEST
);
66 glPolygonMode( GL_FRONT
, GL_FILL
);
67 glEnable(GL_CULL_FACE
);
71 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
72 glShadeModel(GL_SMOOTH
);
73 glEnable(GL_LINE_SMOOTH
);
75 glShadeModel(GL_FLAT
);
76 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_FASTEST
);
81 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
84 glFogi(GL_FOG_MODE
, GL_LINEAR
);
85 GLfloat fog_color
[] = {0,0,0,1};
86 glFogfv(GL_FOG_COLOR
, fog_color
);
87 glFogf(GL_FOG_START
, display
->near_plane
);
88 glFogf(GL_FOG_END
, display
->far_plane
);
93 #ifdef GL_ARB_multisample
94 glEnable(GL_MULTISAMPLE_ARB
);
96 glHint(GL_MULTISAMPLE_FILTER_HINT_NV
,GL_NICEST
);
101 fprintf(stderr
, "SDL ERROR: %s\n", SDL_GetError());
105 void display_world_transform(Display
* display
, Ship
* player
)
107 COPY(display
->cam
, player
->pos
);
108 ADD2(display
->target
, player
->pos
, player
->lookAt
);
109 //display->target[1]=display->target[1]*.5+player->pos[1]*.5;
110 //display->target[2]+=10;
112 display
->cam
[0], display
->cam
[1], display
->cam
[2],
113 display
->target
[0], display
->target
[1], display
->target
[2],
118 void cave_model(Display
* display
, Cave
* cave
, int wire
)
120 for( int i
= 0; i
< SEGMENT_COUNT
-1; ++i
) {
121 int i0
= (cave
->i
+ i
)%SEGMENT_COUNT
;
123 if( (wire
? cave
->gl_wire_list
: cave
->gl_list
)[i0
] == 0 ) {
125 (cave
->gl_wire_list
[i0
] = i0
+ display
->wire_list_start
) :
126 (cave
->gl_list
[i0
] = i0
+ display
->list_start
) ;
128 glNewList( id
, GL_COMPILE
);
130 int i1
= (i0
+ 1)%SEGMENT_COUNT
;
132 glBindTexture(GL_TEXTURE_2D
, display
->texture_id
);
133 glBegin(GL_QUAD_STRIP
);
134 for( int k
= 0; k
<= SECTOR_COUNT
; ++k
) {
136 int k0
= k
%SECTOR_COUNT
;
139 if(i0
==0||i1
==0||k
==3*SECTOR_COUNT
/4)
140 glColor4f(1, 0, 0, 0.5);
142 glColor4f(1, 1, 1, 0.5);
147 (float)(cave
->i
+i
)/SEGMENT_COUNT
,
148 (float)k
/SECTOR_COUNT
);
151 (float)i0
/SEGMENT_COUNT
,
152 1-(float)i0
/SEGMENT_COUNT
,
153 (float)k0
/SECTOR_COUNT
,
156 glVertex3fv(cave
->segs
[i0
][k0
]);
160 ((float)(cave
->i
+i
+1))/SEGMENT_COUNT
,
161 (float)k
/SECTOR_COUNT
);
164 (float)i1
/SEGMENT_COUNT
,
165 1-(float)i1
/SEGMENT_COUNT
,
166 (float)k0
/SECTOR_COUNT
,
169 glVertex3fv(cave
->segs
[i1
][k0
]);
177 glDisable(GL_DEPTH_TEST
);
179 glDisable(GL_TEXTURE_2D
);
181 glEnable(GL_DEPTH_TEST
);
183 glEnable(GL_TEXTURE_2D
);
186 glCallList( (wire
? cave
->gl_wire_list
: cave
->gl_list
)[i0
] );
191 void monolith_model(Display
* display
, Cave
* cave
, Ship
* player
)
193 if(!display
->monoliths
)
198 float w
= MONOLITH_WIDTH
/2;
199 float h
= MONOLITH_HEIGHT
/2;
200 float d
= MONOLITH_DEPTH
;
202 glEnable(GL_DEPTH_TEST
);
204 glDisable(GL_TEXTURE_2D
);
208 glTranslatef( cave
->monolith_x
, cave
->monolith_y
, cave
->segs
[0][0][2] );
209 glRotatef( cave
->monolith_yaw
, 1, 0, 0 );
211 glBegin( GL_QUAD_STRIP
);
212 glVertex3f( +w
, -h
, d
); glVertex3f( -w
, -h
, d
);
213 glVertex3f( +w
, -h
, 0 ); glVertex3f( -w
, -h
, 0 );
214 glVertex3f( +w
, +h
, 0 ); glVertex3f( -w
, +h
, 0 );
215 glVertex3f( +w
, +h
, d
); glVertex3f( -w
, +h
, d
);
219 glVertex3f( -w
, -h
, d
); glVertex3f( -w
, +h
, d
);
220 glVertex3f( -w
, +h
, 0 ); glVertex3f( -w
, -h
, 0 );
222 glVertex3f( +w
, +h
, d
); glVertex3f( +w
, -h
, d
);
223 glVertex3f( +w
, -h
, 0 ); glVertex3f( +w
, +h
, 0 );
229 void ship_model(Display
* display
, Ship
* ship
)
231 if(!display
->cockpit
)
237 float alpha
= (1-MIN(1,(ship
->pos
[2]/MIN_CAVE_RADIUS_DEPTH
)))/8.;
241 float alert_dist
= ship
->radius
*10;
242 float white
= ship
->dist
<= 0 || ship
->dist
> alert_dist
? 1 :
243 1-(alert_dist
- ship
->dist
)/alert_dist
;
247 glDisable(GL_DEPTH_TEST
);
249 glDisable(GL_TEXTURE_2D
);
251 glColor4f(1,white
,white
,alpha
);
253 glTranslatef(0,0,-SHIP_RADIUS
*f
);
254 glCallList( display
->ship_list
);
258 display_world_transform(display
, ship
);
262 ship
->pos
[2]+SHIP_RADIUS
*f
264 glCallList( display
->ship_list
);
268 void render_text(Display
* display
, GLuint id
, const char* text
,
269 float x
, float y
, float w
, float h
,
270 float r
, float g
, float b
)
272 if(text
== NULL
|| text
[0] == '\0')
274 SDL_Color color
= {0xff,0xff,0xff,0xff};
275 SDL_Surface
* label
= TTF_RenderText_Blended(display
->font
, text
, color
);
276 assert(label
!= NULL
);
278 glDisable(GL_DEPTH_TEST
);
280 glEnable(GL_TEXTURE_2D
);
282 glBindTexture(GL_TEXTURE_2D
, id
);
283 gluBuild2DMipmaps(GL_TEXTURE_2D
,
284 GL_RGBA
, label
->w
, label
->h
,
285 GL_RGBA
, GL_UNSIGNED_BYTE
, label
->pixels
);
287 SDL_FreeSurface(label
);
291 glTranslatef(0,0,-2.65); // XXX magic number
292 glBegin(GL_QUAD_STRIP
);
293 glTexCoord2f(0,1); glVertex3f(1-x
*2+w
,1-y
*2-h
,.5);
294 glTexCoord2f(0,0); glVertex3f(1-x
*2+w
,1-y
*2+h
,0);
296 glTexCoord2f(1,1); glVertex3f(1-x
*2-w
,1-y
*2-h
,.5);
297 glTexCoord2f(1,0); glVertex3f(1-x
*2-w
,1-y
*2+h
,0);
302 void display_hud(Display
* display
, Ship
* player
)
304 if(player
->dist
== FLT_MAX
)
307 float max_vel
[3] = { MAX_VEL_X
, MAX_VEL_Y
, MAX_VEL_Z
};
309 log(1+LEN(player
->vel
)-MAX_VEL_Z
) /
310 log(1+LEN(max_vel
)-MAX_VEL_Z
));
316 int score
= (int)player
->pos
[2];
318 #define HUD_TEXT_MAX 80
319 char buf
[HUD_TEXT_MAX
];
320 if(player
->dist
> 0) {
321 snprintf(buf
, HUD_TEXT_MAX
, "velocity %-10s score %9d",
325 if(score
> display
->session_score
)
326 display
->session_score
= score
;
328 snprintf(buf
, HUD_TEXT_MAX
, "velocity %s score %d (%d) - %d",
330 display
->session_score
,
334 if(score
> display
->local_score
) {
335 display
->local_score
= score
;
336 FILE* fp
= fopen(SCORE_FILE
, "w");
338 perror("failed to open score file");
340 fprintf(fp
, "%d", display
->local_score
);
344 if(score
> display
->global_score
) {
345 display
->global_score
= score
;
346 display_net_update(display
);
348 snprintf(buf
, HUD_TEXT_MAX
, "velocity %s score %d (%d/%d/%d)",
350 display
->session_score
,
351 display
->local_score
,
352 display
->global_score
357 float white
= player
->dist
<= 0 ? 1 : 1-vel
;
358 render_text(display
, display
->hud_id
, buf
, .5,.9,1,.2, 1,white
,white
);
361 char display_message_buf
[256];
362 void display_message(Display
* display
, Cave
* cave
, Ship
* player
, const char* buf
)
364 strncpy(display_message_buf
, buf
, sizeof(display_message_buf
)-1);
365 display_message_buf
[sizeof(display_message_buf
)-1] = '\0';
366 display_frame(display
, cave
, player
);
369 void display_start_frame(Display
* display
, float r
, float g
, float b
)
371 glClearColor(r
,g
,b
,1);
372 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
373 glMatrixMode(GL_MODELVIEW
);
377 void display_end_frame(Display
* display
)
381 SDL_GL_SwapBuffers();
384 void display_frame(Display
* display
, Cave
* cave
, Ship
* player
)
386 int hit
= player
->dist
<= SHIP_RADIUS
*1.1;
388 display_start_frame(display
, hit
,0,0);
390 if(!hit
) { // avoid drawing the cave from outside
392 display_world_transform(display
, player
);
393 cave_model(display
, cave
, 0);
394 monolith_model(display
, cave
, player
);
398 ship_model(display
, player
);
399 display_minimap(display
, cave
, player
);
400 display_hud(display
, player
);
401 render_text(display
, display
->msg_id
, display_message_buf
, .5,.5,1,.25, 1,1,1);
403 display_end_frame(display
);
406 GLuint
display_make_ship_list()
408 /* Magic Numbers: It is possible to create a dodecahedron by attaching two pentagons
409 * to each face of a cube. The coordinates of the points are:
410 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
411 * where x = 0.61803398875 and z = 1.61803398875.
413 const float x
= 0.61803398875;
414 const float z
= 1.61803398875;
415 const float a
= 0.525731112119;
416 const float b
= 0.850650808354;
417 const float p
[12][6][3] = {
418 { { 0, a
, b
}, { 0, z
, x
}, { -1, 1, 1 }, { -x
, 0, z
}, { x
, 0, z
}, { 1, 1, 1 } },
419 { { 0, a
, -b
}, { 0, z
, -x
}, { 1, 1, -1 }, { x
, 0, -z
}, { -x
, 0, -z
}, { -1, 1, -1 } },
420 { { 0, -a
, b
}, { 0, -z
, x
}, { 1, -1, 1 }, { x
, 0, z
}, { -x
, 0, z
}, { -1, -1, 1 } },
421 { { 0, -a
, -b
}, { 0, -z
, -x
}, { -1, -1, -1 }, { -x
, 0, -z
}, { x
, 0, -z
}, { 1, -1, -1 } },
423 { { b
, 0, a
}, { x
, 0, z
}, { 1, -1, 1 }, { z
, -x
, 0 }, { z
, x
, 0 }, { 1, 1, 1 } },
424 { { -b
, 0, a
}, { -x
, 0, z
}, { -1, 1, 1 }, { -z
, x
, 0 }, { -z
, -x
, 0 }, { -1, -1, 1 } },
425 { { b
, 0, -a
}, { x
, 0, -z
}, { 1, 1, -1 }, { z
, x
, 0 }, { z
, -x
, 0 }, { 1, -1, -1 } },
426 { { -b
, 0, -a
}, { -x
, 0, -z
}, { -1, -1, -1 }, { -z
, -x
, 0 }, { -z
, x
, 0 }, { -1, 1, -1 } },
428 { { a
, b
, 0 }, { z
, x
, 0 }, { 1, 1, -1 }, { 0, z
, -x
}, { 0, z
, x
}, { 1, 1, 1 } },
429 { { a
, -b
, 0 }, { z
, -x
, 0 }, { 1, -1, 1 }, { 0, -z
, x
}, { 0, -z
, -x
}, { 1, -1, -1 } },
430 { { -a
, b
, 0 }, { -z
, x
, 0 }, { -1, 1, 1 }, { 0, z
, x
}, { 0, z
, -x
}, { -1, 1, -1 } },
431 { { -a
, -b
, 0 }, { -z
, -x
, 0 }, { -1, -1, -1 }, { 0, -z
, -x
}, { 0, -z
, x
}, { -1, -1, 1 } }
434 GLuint ship_list
= glGenLists (SEGMENT_COUNT
);
435 glNewList (ship_list
, GL_COMPILE
);
436 for (int j
= 0; j
< 12; j
++) {
437 glBegin (GL_LINE_LOOP
);
438 glNormal3fv (p
[j
][0]);
439 for (int i
= 1; i
< 6; i
++)
440 glVertex3fv (p
[j
][i
]);
448 void display_init(Display
* display
, Args
* args
)
451 memset(display
, 0, sizeof(Display
));
453 if(SDL_Init(SDL_INIT_VIDEO
) != 0) {
454 fprintf(stderr
, "SDL_Init(): %s\n", SDL_GetError());
459 display
->near_plane
= MIN(SEGMENT_LEN
,SHIP_RADIUS
)/4.; // was EPSILON;
460 display
->far_plane
= SEGMENT_COUNT
* SEGMENT_LEN
;
461 SET(display
->cam
, 0,0,0);
462 SET(display
->target
, 0,0,1);
465 int h
= args
->height
;
466 int f
= args
->fullscreen
;
468 #if SDL_VERSION_ATLEAST(1,2,11)
469 const SDL_VideoInfo
* info
= SDL_GetVideoInfo();
470 assert(info
!= NULL
);
479 viewport(display
, w
, h
, args
->bpp
, f
, args
->antialiasing
);
481 display
->list_start
= glGenLists( SEGMENT_COUNT
);
482 display
->wire_list_start
= glGenLists( SEGMENT_COUNT
);
484 if(TTF_Init() != 0) {
485 fprintf(stderr
, "TTF_Init(): %s\n", TTF_GetError());
490 char* font_filename
= FONT_FILE
;
491 int font_size
= args
->antialiasing
? 96 : 48;
492 display
->font
= TTF_OpenFont(font_filename
, font_size
); // FIXME path
493 if(display
->font
== NULL
) {
494 fprintf(stderr
, "TTF_OpenFont(%s): %s\n", font_filename
, TTF_GetError());
498 glGenTextures(1, &display
->hud_id
);
499 glBindTexture(GL_TEXTURE_2D
, display
->hud_id
);
500 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
501 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
502 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
503 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
505 glGenTextures(1, &display
->msg_id
);
506 glBindTexture(GL_TEXTURE_2D
, display
->msg_id
);
507 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
508 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
509 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
510 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
512 display_start_frame(display
, 0,0,0);
513 render_text(display
, display
->msg_id
, "loading cave9", .5,.5,1,.25, 1,1,1);
514 display_end_frame(display
);
516 char* texture_filename
= TEXTURE_FILE
;
518 glGenTextures(1, &display
->texture_id
);
519 glBindTexture(GL_TEXTURE_2D
, display
->texture_id
);
520 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR_MIPMAP_LINEAR
);
521 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
523 SDL_Surface
* texture
= IMG_Load(texture_filename
);
524 if(texture
== NULL
) {
525 fprintf(stderr
, "IMG_Load(%s): %s\n", texture_filename
, IMG_GetError());
529 GLenum err
= gluBuild2DMipmaps(GL_TEXTURE_2D
,
530 GL_RGB
, texture
->w
, texture
->h
,
531 GL_RGB
, GL_UNSIGNED_BYTE
,texture
->pixels
);
533 fprintf(stderr
, "gluBuild2DMipmaps(): %s\n", gluErrorString(err
));
537 SDL_FreeSurface(texture
);
539 display
->ship_list
= display_make_ship_list();
541 display
->monoliths
= args
->monoliths
;
542 display
->cockpit
= args
->cockpit
;
544 display
->session_score
= 0;
546 display
->local_score
= 0;
547 FILE* fp
= fopen(SCORE_FILE
, "r");
549 perror("failed to open score file");
551 fscanf(fp
, "%d", &display
->local_score
);
555 display
->global_score
= 0;
557 if(SDLNet_Init()==-1)
559 fprintf(stderr
, "SDLNet_Init(): %s\n",SDLNet_GetError());
565 display
->udp_sock
= 0;
566 display
->udp_pkt
= NULL
;
567 if(SDLNet_ResolveHost(&addr
,GLOBAL_SCORE_HOST
, GLOBAL_SCORE_PORT
) == -1) {
568 fprintf(stderr
, "SDLNet_ResolveHost(): %s\n", SDLNet_GetError());
570 display
->udp_sock
=SDLNet_UDP_Open(0);
571 if(display
->udp_sock
== 0) {
572 fprintf(stderr
, "SDLNet_UDP_Open(): %s\n", SDLNet_GetError());
573 display_net_finish(display
);
575 if(SDLNet_UDP_Bind(display
->udp_sock
, 0, &addr
) == -1) {
576 fprintf(stderr
, "SDLNet_UDP_Bind(): %s\n", SDLNet_GetError());
577 display_net_finish(display
);
579 display
->udp_pkt
= SDLNet_AllocPacket(GLOBAL_SCORE_LEN
);
580 if(display
->udp_pkt
== NULL
) {
581 display_net_finish(display
);
589 void display_net_update(Display
* display
)
591 if(display
->udp_sock
== 0)
594 snprintf((char*)display
->udp_pkt
->data
,GLOBAL_SCORE_LEN
,"%d",display
->global_score
);
595 display
->udp_pkt
->len
= GLOBAL_SCORE_LEN
;
596 if(SDLNet_UDP_Send(display
->udp_sock
,0,display
->udp_pkt
) == 0) {
597 fprintf(stderr
, "SDLNet_UDP_Send(): %s\n", SDLNet_GetError());
599 SDL_Delay(666); // XXX only wait 666ms for hiscores
600 if(SDLNet_UDP_Recv(display
->udp_sock
,display
->udp_pkt
) == 0) {
601 fprintf(stderr
, "SDLNet_UDP_Recv(%s,%d): %s\n",
602 GLOBAL_SCORE_HOST
, GLOBAL_SCORE_PORT
, SDLNet_GetError());
604 sscanf((char*)display
->udp_pkt
->data
,"%d",&display
->global_score
);
609 void display_net_finish(Display
* display
)
611 if(display
->udp_pkt
!= NULL
) {
612 SDLNet_FreePacket(display
->udp_pkt
);
613 display
->udp_pkt
= NULL
;
615 if(display
->udp_sock
!= 0) {
616 SDLNet_UDP_Close(display
->udp_sock
);
617 display
->udp_sock
= 0;
621 void display_minimap(Display
* display
, Cave
* cave
, Ship
* player
)
624 glScalef(.0065,.003,.001);
625 glRotatef(-90,0,1,0);
627 -player
->pos
[0]-1000, // XXX hardcoded
629 -player
->pos
[2]-(SEGMENT_COUNT
-1)*SEGMENT_LEN
/2);
630 cave_model(display
, cave
, 1);
635 // vim600:fdm=syntax:fdn=1: