2 This file is part of cave9.
4 cave9 is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 cave9 is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with cave9. If not, see <http://www.gnu.org/licenses/>.
18 #include <SDL_image.h>
19 #include <SDL_opengl.h>
29 void viewport(Display
* display
, GLsizei w
, GLsizei h
, GLsizei bpp
,
30 bool fullscreen
, int aa
=0)
33 SDL_GL_SetAttribute( SDL_GL_RED_SIZE
, bpp
/3 );
34 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE
, bpp
/3 );
35 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE
, bpp
/3 );
36 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE
, 16 );
37 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER
, 1 );
40 SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS
, 1 );
41 SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES
, aa
);
44 int flags
= SDL_HWSURFACE
|SDL_OPENGLBLIT
|SDL_RESIZABLE
;
46 flags
|= SDL_FULLSCREEN
;
47 display
->screen
= SDL_SetVideoMode(w
, h
, bpp
, flags
);
48 if(display
->screen
== NULL
)
51 bpp
= display
->screen
->format
->BitsPerPixel
;
52 //printf("%dx%dx%d\n", display->screen->w, display->screen->h, display->screen->format->BitsPerPixel);
57 SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS
, &arg
);
58 printf("SDL_GL_MULTISAMPLEBUFFERS %d\n", arg
);
59 SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES
, &arg
);
60 printf("SDL_GL_MULTISAMPLESAMPLES %d\n", arg
);
64 SDL_WM_SetCaption("cave9 -- 9hells.org", "cave9");
65 SDL_ShowCursor(SDL_DISABLE
);
70 glMatrixMode(GL_PROJECTION
);
72 gluPerspective(45,-w
/(GLfloat
)h
,display
->near_plane
,display
->far_plane
);
76 glClearColor(0,0,0,0);
79 glDepthFunc(GL_LEQUAL
);
80 glEnable(GL_DEPTH_TEST
);
82 glPolygonMode( GL_FRONT
, GL_FILL
);
83 glEnable(GL_CULL_FACE
);
87 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
88 glShadeModel(GL_SMOOTH
);
89 glEnable(GL_LINE_SMOOTH
);
91 glShadeModel(GL_FLAT
);
92 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_FASTEST
);
97 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
100 glFogi(GL_FOG_MODE
, GL_LINEAR
);
101 GLfloat fog_color
[] = {0,0,0,1};
102 glFogfv(GL_FOG_COLOR
, fog_color
);
103 glFogf(GL_FOG_START
, display
->near_plane
);
104 glFogf(GL_FOG_END
, display
->far_plane
);
109 #ifdef GL_ARB_multisample
110 glEnable(GL_MULTISAMPLE_ARB
);
112 glHint(GL_MULTISAMPLE_FILTER_HINT_NV
,GL_NICEST
);
117 fprintf(stderr
, "SDL ERROR: %s\n", SDL_GetError());
121 void display_world_transform(Display
* display
, Ship
* player
)
123 COPY(display
->cam
, player
->pos
);
124 ADD2(display
->target
, player
->pos
, player
->lookAt
);
125 //display->target[1]=display->target[1]*.5+player->pos[1]*.5;
126 //display->target[2]+=10;
128 display
->cam
[0], display
->cam
[1], display
->cam
[2],
129 display
->target
[0], display
->target
[1], display
->target
[2],
134 void cave_model(Display
* display
, Cave
* cave
, int wire
)
136 for( int i
= 0; i
< SEGMENT_COUNT
-1; ++i
) {
137 int i0
= (cave
->i
+ i
)%SEGMENT_COUNT
;
139 if( (wire
? cave
->gl_wire_list
: cave
->gl_list
)[i0
] == 0 ) {
141 (cave
->gl_wire_list
[i0
] = i0
+ display
->wire_list_start
) :
142 (cave
->gl_list
[i0
] = i0
+ display
->list_start
) ;
144 glNewList( id
, GL_COMPILE
);
146 int i1
= (i0
+ 1)%SEGMENT_COUNT
;
148 glBindTexture(GL_TEXTURE_2D
, display
->texture_id
);
149 glBegin(GL_QUAD_STRIP
);
150 for( int k
= 0; k
<= SECTOR_COUNT
; ++k
) {
152 int k0
= k
%SECTOR_COUNT
;
155 if(i0
==0||i1
==0||k
==3*SECTOR_COUNT
/4)
156 glColor4f(1, 0, 0, 0.5);
158 glColor4f(1, 1, 1, 0.5);
163 (float)(cave
->i
+i
)/SEGMENT_COUNT
,
164 (float)k
/SECTOR_COUNT
);
167 (float)i0
/SEGMENT_COUNT
,
168 1-(float)i0
/SEGMENT_COUNT
,
169 (float)k0
/SECTOR_COUNT
,
172 glVertex3fv(cave
->segs
[i0
][k0
]);
176 ((float)(cave
->i
+i
+1))/SEGMENT_COUNT
,
177 (float)k
/SECTOR_COUNT
);
180 (float)i1
/SEGMENT_COUNT
,
181 1-(float)i1
/SEGMENT_COUNT
,
182 (float)k0
/SECTOR_COUNT
,
185 glVertex3fv(cave
->segs
[i1
][k0
]);
193 glDisable(GL_DEPTH_TEST
);
195 glDisable(GL_TEXTURE_2D
);
197 glEnable(GL_DEPTH_TEST
);
199 glEnable(GL_TEXTURE_2D
);
202 glCallList( (wire
? cave
->gl_wire_list
: cave
->gl_list
)[i0
] );
207 void monolith_model(Display
* display
, Cave
* cave
, Ship
* player
)
209 if(!display
->monoliths
)
214 float w
= MONOLITH_WIDTH
/2;
215 float h
= MONOLITH_HEIGHT
/2;
216 float d
= MONOLITH_DEPTH
;
218 glEnable(GL_DEPTH_TEST
);
220 glDisable(GL_TEXTURE_2D
);
224 glTranslatef( cave
->monolith_x
, cave
->monolith_y
, cave
->segs
[0][0][2] );
225 glRotatef( cave
->monolith_yaw
, 1, 0, 0 );
227 glBegin( GL_QUAD_STRIP
);
228 glVertex3f( +w
, -h
, d
); glVertex3f( -w
, -h
, d
);
229 glVertex3f( +w
, -h
, 0 ); glVertex3f( -w
, -h
, 0 );
230 glVertex3f( +w
, +h
, 0 ); glVertex3f( -w
, +h
, 0 );
231 glVertex3f( +w
, +h
, d
); glVertex3f( -w
, +h
, d
);
235 glVertex3f( -w
, -h
, d
); glVertex3f( -w
, +h
, d
);
236 glVertex3f( -w
, +h
, 0 ); glVertex3f( -w
, -h
, 0 );
238 glVertex3f( +w
, +h
, d
); glVertex3f( +w
, -h
, d
);
239 glVertex3f( +w
, -h
, 0 ); glVertex3f( +w
, +h
, 0 );
245 void ship_model(Display
* display
, Ship
* ship
)
247 if(!display
->cockpit
)
253 float alpha
= (1-MIN(1,(ship
->pos
[2]/MIN_CAVE_RADIUS_DEPTH
)))/8.;
257 float alert_dist
= ship
->radius
*10;
258 float white
= ship
->dist
<= 0 || ship
->dist
> alert_dist
? 1 :
259 1-(alert_dist
- ship
->dist
)/alert_dist
;
263 glDisable(GL_DEPTH_TEST
);
265 glDisable(GL_TEXTURE_2D
);
267 glColor4f(1,white
,white
,alpha
);
269 glTranslatef(0,0,-SHIP_RADIUS
*f
);
270 glCallList( display
->ship_list
);
274 display_world_transform(display
, ship
);
278 ship
->pos
[2]+SHIP_RADIUS
*f
280 glCallList( display
->ship_list
);
284 void render_text(Display
* display
, GLuint id
, const char* text
,
285 float x
, float y
, float w
, float h
,
286 float r
, float g
, float b
)
288 if(text
== NULL
|| text
[0] == '\0')
290 SDL_Color color
= {0xff,0xff,0xff,0xff};
291 SDL_Surface
* label
= TTF_RenderText_Blended(display
->font
, text
, color
);
292 assert(label
!= NULL
);
294 glDisable(GL_DEPTH_TEST
);
296 glEnable(GL_TEXTURE_2D
);
298 glBindTexture(GL_TEXTURE_2D
, id
);
299 gluBuild2DMipmaps(GL_TEXTURE_2D
,
300 GL_RGBA
, label
->w
, label
->h
,
301 GL_RGBA
, GL_UNSIGNED_BYTE
, label
->pixels
);
303 SDL_FreeSurface(label
);
307 glTranslatef(0,0,-2.65); // XXX magic number
308 glBegin(GL_QUAD_STRIP
);
309 glTexCoord2f(0,1); glVertex3f(1-x
*2+w
,1-y
*2-h
,.5);
310 glTexCoord2f(0,0); glVertex3f(1-x
*2+w
,1-y
*2+h
,0);
312 glTexCoord2f(1,1); glVertex3f(1-x
*2-w
,1-y
*2-h
,.5);
313 glTexCoord2f(1,0); glVertex3f(1-x
*2-w
,1-y
*2+h
,0);
318 void display_hud(Display
* display
, Ship
* player
)
320 if(player
->dist
== FLT_MAX
)
323 float max_vel
[3] = { MAX_VEL_X
, MAX_VEL_Y
, MAX_VEL_Z
};
325 log(1+LEN(player
->vel
)-MAX_VEL_Z
) /
326 log(1+LEN(max_vel
)-MAX_VEL_Z
));
332 int score
= (int)player
->pos
[2];
334 #define HUD_TEXT_MAX 80
335 char buf
[HUD_TEXT_MAX
];
336 if(player
->dist
> 0) {
337 snprintf(buf
, HUD_TEXT_MAX
, "velocity %-10s score %9d",
341 if(score
> display
->session_score
)
342 display
->session_score
= score
;
344 snprintf(buf
, HUD_TEXT_MAX
, "velocity %s score %d (%d) - %d",
346 display
->session_score
,
350 if(score
> display
->local_score
) {
351 display
->local_score
= score
;
352 FILE* fp
= fopen(SCORE_FILE
, "w");
354 perror("failed to open score file");
356 fprintf(fp
, "%d", display
->local_score
);
360 if(score
> display
->global_score
) {
361 display
->global_score
= score
;
362 display_net_update(display
);
364 snprintf(buf
, HUD_TEXT_MAX
, "velocity %s score %d (%d/%d/%d)",
366 display
->session_score
,
367 display
->local_score
,
368 display
->global_score
373 float white
= player
->dist
<= 0 ? 1 : 1-vel
;
374 render_text(display
, display
->hud_id
, buf
, .5,.9,1,.2, 1,white
,white
);
377 char display_message_buf
[256];
378 void display_message(Display
* display
, Cave
* cave
, Ship
* player
, const char* buf
)
380 strncpy(display_message_buf
, buf
, sizeof(display_message_buf
)-1);
381 display_message_buf
[sizeof(display_message_buf
)-1] = '\0';
382 display_frame(display
, cave
, player
);
385 void display_start_frame(Display
* display
, float r
, float g
, float b
)
387 glClearColor(r
,g
,b
,1);
388 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
389 glMatrixMode(GL_MODELVIEW
);
393 void display_end_frame(Display
* display
)
397 SDL_GL_SwapBuffers();
400 void display_frame(Display
* display
, Cave
* cave
, Ship
* player
)
402 int hit
= player
->dist
<= SHIP_RADIUS
*1.1;
404 display_start_frame(display
, hit
,0,0);
406 if(!hit
) { // avoid drawing the cave from outside
408 display_world_transform(display
, player
);
409 cave_model(display
, cave
, 0);
410 monolith_model(display
, cave
, player
);
414 ship_model(display
, player
);
415 display_minimap(display
, cave
, player
);
416 display_hud(display
, player
);
417 render_text(display
, display
->msg_id
, display_message_buf
, .5,.5,1,.25, 1,1,1);
419 display_end_frame(display
);
422 GLuint
display_make_ship_list()
424 /* Magic Numbers: It is possible to create a dodecahedron by attaching two pentagons
425 * to each face of a cube. The coordinates of the points are:
426 * (+-x,0, z); (+-1, 1, 1); (0, z, x )
427 * where x = 0.61803398875 and z = 1.61803398875.
429 const float x
= 0.61803398875;
430 const float z
= 1.61803398875;
431 const float a
= 0.525731112119;
432 const float b
= 0.850650808354;
433 const float p
[12][6][3] = {
434 { { 0, a
, b
}, { 0, z
, x
}, { -1, 1, 1 }, { -x
, 0, z
}, { x
, 0, z
}, { 1, 1, 1 } },
435 { { 0, a
, -b
}, { 0, z
, -x
}, { 1, 1, -1 }, { x
, 0, -z
}, { -x
, 0, -z
}, { -1, 1, -1 } },
436 { { 0, -a
, b
}, { 0, -z
, x
}, { 1, -1, 1 }, { x
, 0, z
}, { -x
, 0, z
}, { -1, -1, 1 } },
437 { { 0, -a
, -b
}, { 0, -z
, -x
}, { -1, -1, -1 }, { -x
, 0, -z
}, { x
, 0, -z
}, { 1, -1, -1 } },
439 { { b
, 0, a
}, { x
, 0, z
}, { 1, -1, 1 }, { z
, -x
, 0 }, { z
, x
, 0 }, { 1, 1, 1 } },
440 { { -b
, 0, a
}, { -x
, 0, z
}, { -1, 1, 1 }, { -z
, x
, 0 }, { -z
, -x
, 0 }, { -1, -1, 1 } },
441 { { b
, 0, -a
}, { x
, 0, -z
}, { 1, 1, -1 }, { z
, x
, 0 }, { z
, -x
, 0 }, { 1, -1, -1 } },
442 { { -b
, 0, -a
}, { -x
, 0, -z
}, { -1, -1, -1 }, { -z
, -x
, 0 }, { -z
, x
, 0 }, { -1, 1, -1 } },
444 { { a
, b
, 0 }, { z
, x
, 0 }, { 1, 1, -1 }, { 0, z
, -x
}, { 0, z
, x
}, { 1, 1, 1 } },
445 { { a
, -b
, 0 }, { z
, -x
, 0 }, { 1, -1, 1 }, { 0, -z
, x
}, { 0, -z
, -x
}, { 1, -1, -1 } },
446 { { -a
, b
, 0 }, { -z
, x
, 0 }, { -1, 1, 1 }, { 0, z
, x
}, { 0, z
, -x
}, { -1, 1, -1 } },
447 { { -a
, -b
, 0 }, { -z
, -x
, 0 }, { -1, -1, -1 }, { 0, -z
, -x
}, { 0, -z
, x
}, { -1, -1, 1 } }
450 GLuint ship_list
= glGenLists (SEGMENT_COUNT
);
451 glNewList (ship_list
, GL_COMPILE
);
452 for (int j
= 0; j
< 12; j
++) {
453 glBegin (GL_LINE_LOOP
);
454 glNormal3fv (p
[j
][0]);
455 for (int i
= 1; i
< 6; i
++)
456 glVertex3fv (p
[j
][i
]);
464 void display_init(Display
* display
, Args
* args
)
467 memset(display
, 0, sizeof(Display
));
469 if(SDL_Init(SDL_INIT_VIDEO
) != 0) {
470 fprintf(stderr
, "SDL_Init(): %s\n", SDL_GetError());
475 display
->near_plane
= MIN(SEGMENT_LEN
,SHIP_RADIUS
)/4.; // was EPSILON;
476 display
->far_plane
= SEGMENT_COUNT
* SEGMENT_LEN
;
477 SET(display
->cam
, 0,0,0);
478 SET(display
->target
, 0,0,1);
481 int h
= args
->height
;
482 int f
= args
->fullscreen
;
484 #if SDL_VERSION_ATLEAST(1,2,11)
485 const SDL_VideoInfo
* info
= SDL_GetVideoInfo();
486 assert(info
!= NULL
);
495 viewport(display
, w
, h
, args
->bpp
, f
, args
->antialiasing
);
497 display
->list_start
= glGenLists( SEGMENT_COUNT
);
498 display
->wire_list_start
= glGenLists( SEGMENT_COUNT
);
500 if(TTF_Init() != 0) {
501 fprintf(stderr
, "TTF_Init(): %s\n", TTF_GetError());
506 char* font_filename
= FONT_FILE
;
507 int font_size
= args
->antialiasing
? 96 : 48;
508 display
->font
= TTF_OpenFont(font_filename
, font_size
); // FIXME path
509 if(display
->font
== NULL
) {
510 fprintf(stderr
, "TTF_OpenFont(%s): %s\n", font_filename
, TTF_GetError());
514 glGenTextures(1, &display
->hud_id
);
515 glBindTexture(GL_TEXTURE_2D
, display
->hud_id
);
516 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
517 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
518 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
519 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
521 glGenTextures(1, &display
->msg_id
);
522 glBindTexture(GL_TEXTURE_2D
, display
->msg_id
);
523 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
524 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
525 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
526 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
528 display_start_frame(display
, 0,0,0);
529 render_text(display
, display
->msg_id
, "loading cave9", .5,.5,1,.25, 1,1,1);
530 display_end_frame(display
);
532 char* texture_filename
= TEXTURE_FILE
;
534 glGenTextures(1, &display
->texture_id
);
535 glBindTexture(GL_TEXTURE_2D
, display
->texture_id
);
536 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR_MIPMAP_LINEAR
);
537 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
539 SDL_Surface
* texture
= IMG_Load(texture_filename
);
540 if(texture
== NULL
) {
541 fprintf(stderr
, "IMG_Load(%s): %s\n", texture_filename
, IMG_GetError());
545 GLenum err
= gluBuild2DMipmaps(GL_TEXTURE_2D
,
546 GL_RGB
, texture
->w
, texture
->h
,
547 GL_RGB
, GL_UNSIGNED_BYTE
,texture
->pixels
);
549 fprintf(stderr
, "gluBuild2DMipmaps(): %s\n", gluErrorString(err
));
553 SDL_FreeSurface(texture
);
555 display
->ship_list
= display_make_ship_list();
557 display
->monoliths
= args
->monoliths
;
558 display
->cockpit
= args
->cockpit
;
560 display
->session_score
= 0;
562 display
->local_score
= 0;
563 FILE* fp
= fopen(SCORE_FILE
, "r");
565 perror("failed to open score file");
567 fscanf(fp
, "%d", &display
->local_score
);
571 display
->global_score
= 0;
573 if(SDLNet_Init()==-1)
575 fprintf(stderr
, "SDLNet_Init(): %s\n",SDLNet_GetError());
581 display
->udp_sock
= 0;
582 display
->udp_pkt
= NULL
;
583 if(SDLNet_ResolveHost(&addr
,GLOBAL_SCORE_HOST
, GLOBAL_SCORE_PORT
) == -1) {
584 fprintf(stderr
, "SDLNet_ResolveHost(): %s\n", SDLNet_GetError());
586 display
->udp_sock
=SDLNet_UDP_Open(0);
587 if(display
->udp_sock
== 0) {
588 fprintf(stderr
, "SDLNet_UDP_Open(): %s\n", SDLNet_GetError());
589 display_net_finish(display
);
591 if(SDLNet_UDP_Bind(display
->udp_sock
, 0, &addr
) == -1) {
592 fprintf(stderr
, "SDLNet_UDP_Bind(): %s\n", SDLNet_GetError());
593 display_net_finish(display
);
595 display
->udp_pkt
= SDLNet_AllocPacket(GLOBAL_SCORE_LEN
);
596 if(display
->udp_pkt
== NULL
) {
597 display_net_finish(display
);
605 void display_net_update(Display
* display
)
607 if(display
->udp_sock
== 0)
610 snprintf((char*)display
->udp_pkt
->data
,GLOBAL_SCORE_LEN
,"%d",display
->global_score
);
611 display
->udp_pkt
->len
= GLOBAL_SCORE_LEN
;
612 if(SDLNet_UDP_Send(display
->udp_sock
,0,display
->udp_pkt
) == 0) {
613 fprintf(stderr
, "SDLNet_UDP_Send(): %s\n", SDLNet_GetError());
615 SDL_Delay(666); // XXX only wait 666ms for hiscores
616 if(SDLNet_UDP_Recv(display
->udp_sock
,display
->udp_pkt
) == 0) {
617 fprintf(stderr
, "SDLNet_UDP_Recv(%s,%d): %s\n",
618 GLOBAL_SCORE_HOST
, GLOBAL_SCORE_PORT
, SDLNet_GetError());
620 sscanf((char*)display
->udp_pkt
->data
,"%d",&display
->global_score
);
625 void display_net_finish(Display
* display
)
627 if(display
->udp_pkt
!= NULL
) {
628 SDLNet_FreePacket(display
->udp_pkt
);
629 display
->udp_pkt
= NULL
;
631 if(display
->udp_sock
!= 0) {
632 SDLNet_UDP_Close(display
->udp_sock
);
633 display
->udp_sock
= 0;
637 void display_minimap(Display
* display
, Cave
* cave
, Ship
* player
)
640 glScalef(.0065,.003,.001);
641 glRotatef(-90,0,1,0);
643 -player
->pos
[0]-1000, // XXX hardcoded
645 -player
->pos
[2]-(SEGMENT_COUNT
-1)*SEGMENT_LEN
/2);
646 cave_model(display
, cave
, 1);
651 // vim600:fdm=syntax:fdn=1: