ship list further de-uglyfied
[cave9.git] / src / display.cpp
blob680eb98d3a09bb7d829397002484dd57d4c337f4
2 #include <SDL_image.h>
3 #include <SDL_opengl.h>
4 #include <GL/gl.h>
5 #include <GL/glu.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <math.h>
9 #include <float.h>
10 #include <assert.h>
11 #include "display.h"
13 void viewport(Display* display, GLsizei w, GLsizei h, GLsizei bpp,
14 bool fullscreen, int aa=0)
16 // video mode
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 );
23 if(aa) {
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;
29 if(fullscreen)
30 flags |= SDL_FULLSCREEN;
31 display->screen = SDL_SetVideoMode(w, h, bpp, flags);
32 if(display->screen == NULL)
33 goto error;
35 bpp = display->screen->format->BitsPerPixel;
36 //printf("%dx%dx%d\n", display->screen->w, display->screen->h, display->screen->format->BitsPerPixel);
38 #if 0
39 if(aa) {
40 int arg;
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);
46 #endif
48 SDL_WM_SetCaption("cave9 -- 9hells.org", "cave9");
49 SDL_ShowCursor(SDL_DISABLE);
51 // projection
52 glViewport(0,0,w,h);
54 glMatrixMode(GL_PROJECTION);
55 glLoadIdentity();
56 gluPerspective(45,-w/(GLfloat)h,display->near_plane,display->far_plane);
59 // settings
60 glClearColor(0,0,0,0);
61 glClearDepth(1);
63 glDepthFunc(GL_LEQUAL);
64 glEnable(GL_DEPTH_TEST);
66 glPolygonMode( GL_FRONT, GL_FILL );
67 glEnable(GL_CULL_FACE);
68 glCullFace(GL_FRONT);
70 if(aa) {
71 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
72 glShadeModel(GL_SMOOTH);
73 glEnable(GL_LINE_SMOOTH);
74 } else {
75 glShadeModel(GL_FLAT);
76 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
79 glLineWidth(16);
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);
89 glEnable(GL_FOG);
92 if(aa) {
93 #ifdef GL_ARB_multisample
94 glEnable(GL_MULTISAMPLE_ARB);
95 #endif
96 glHint(GL_MULTISAMPLE_FILTER_HINT_NV,GL_NICEST);
99 return;
100 error:
101 fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
102 exit(1);
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;
111 gluLookAt(
112 display->cam[0], display->cam[1], display->cam[2],
113 display->target[0], display->target[1], display->target[2],
114 0,1,0
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 ) {
124 int id = wire ?
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;
131 if(!wire)
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;
138 if(!wire) {
139 if(i0==0||i1==0||k==3*SECTOR_COUNT/4)
140 glColor4f(1, 0, 0, 0.5);
141 else
142 glColor4f(1, 1, 1, 0.5);
145 if(!wire) {
146 glTexCoord2f(
147 (float)(cave->i+i)/SEGMENT_COUNT,
148 (float)k/SECTOR_COUNT);
149 } else {
150 glColor4f(
151 (float)i0/SEGMENT_COUNT,
152 1-(float)i0/SEGMENT_COUNT,
153 (float)k0/SECTOR_COUNT,
154 0.5);
156 glVertex3fv(cave->segs[i0][k0]);
158 if(!wire) {
159 glTexCoord2f(
160 ((float)(cave->i+i+1))/SEGMENT_COUNT,
161 (float)k/SECTOR_COUNT);
162 } else {
163 glColor4f(
164 (float)i1/SEGMENT_COUNT,
165 1-(float)i1/SEGMENT_COUNT,
166 (float)k0/SECTOR_COUNT,
167 0.5);
169 glVertex3fv(cave->segs[i1][k0]);
171 glEnd();
173 glEndList();
176 if(wire) {
177 glDisable(GL_DEPTH_TEST);
178 glEnable(GL_BLEND);
179 glDisable(GL_TEXTURE_2D);
180 } else {
181 glEnable(GL_DEPTH_TEST);
182 glDisable(GL_BLEND);
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)
194 return;
196 glColor3f(.2,.2,.2);
198 float w = MONOLITH_WIDTH/2;
199 float h = MONOLITH_HEIGHT/2;
200 float d = MONOLITH_DEPTH;
202 glEnable(GL_DEPTH_TEST);
203 glDisable(GL_BLEND);
204 glDisable(GL_TEXTURE_2D);
206 glPushMatrix();
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 );
216 glEnd();
218 glBegin( GL_QUADS );
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 );
224 glEnd();
226 glPopMatrix();
229 void ship_model(Display* display, Ship* ship)
231 if(!display->cockpit)
232 return;
234 if(ship->dist <= 0)
235 return;
237 float alpha = (1-MIN(1,(ship->pos[2]/MIN_CAVE_RADIUS_DEPTH)))/8.;
238 if(alpha == 0)
239 return;
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;
245 float f =1.8;
247 glDisable(GL_DEPTH_TEST);
248 glEnable(GL_BLEND);
249 glDisable(GL_TEXTURE_2D);
251 glColor4f(1,white,white,alpha);
252 glPushMatrix();
253 glTranslatef(0,0,-SHIP_RADIUS*f);
254 glCallList( display->ship_list );
255 glPopMatrix();
257 glPushMatrix();
258 display_world_transform(display, ship);
259 glTranslatef(
260 ship->pos[0],
261 ship->pos[1],
262 ship->pos[2]+SHIP_RADIUS*f
264 glCallList( display->ship_list );
265 glPopMatrix();
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')
273 return;
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);
279 glEnable(GL_BLEND);
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);
289 glPushMatrix();
290 glColor3f(r,g,b);
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);
298 glEnd();
299 glPopMatrix();
302 void display_hud(Display* display, Ship* player)
304 if(player->dist == FLT_MAX)
305 return;
307 float max_vel[3] = { MAX_VEL_X, MAX_VEL_Y, MAX_VEL_Z };
308 float vel = MIN(1,
309 log(1+LEN(player->vel)-MAX_VEL_Z) /
310 log(1+LEN(max_vel)-MAX_VEL_Z));
311 char gauge[11];
312 int i = int(vel*10);
313 memset(gauge,'/',i);
314 gauge[i] = '\0';
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",
322 gauge, score
324 } else {
325 if(score > display->session_score)
326 display->session_score = score;
327 if(player->start) {
328 snprintf(buf, HUD_TEXT_MAX, "velocity %s score %d (%d) - %d",
329 gauge, score,
330 display->session_score,
331 (int)player->start
333 } else {
334 if(score > display->local_score) {
335 display->local_score = score;
336 FILE* fp = fopen(SCORE_FILE, "w");
337 if(fp == NULL) {
338 perror("failed to open score file");
339 } else {
340 fprintf(fp, "%d", display->local_score);
341 fclose(fp);
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)",
349 gauge, score,
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);
374 glLoadIdentity();
377 void display_end_frame(Display* display)
379 glFinish();
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
391 glPushMatrix();
392 display_world_transform(display, player);
393 cave_model(display, cave, 0);
394 monolith_model(display, cave, player);
395 glPopMatrix();
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]);
441 glEnd();
443 glEndList();
445 return ship_list;
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());
455 exit(1);
457 atexit(SDL_Quit);
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);
464 int w = args->width;
465 int h = args->height;
466 int f = args->fullscreen;
467 if(args->highres) {
468 #if SDL_VERSION_ATLEAST(1,2,11)
469 const SDL_VideoInfo* info = SDL_GetVideoInfo();
470 assert(info != NULL);
471 w = info->current_w;
472 h = info->current_h;
473 #else
474 w = 1024;
475 h = 768;
476 #endif
477 f = 1;
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());
486 exit(1);
488 atexit(TTF_Quit);
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());
495 exit(1);
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());
526 exit(1);
529 GLenum err = gluBuild2DMipmaps(GL_TEXTURE_2D,
530 GL_RGB, texture->w, texture->h,
531 GL_RGB, GL_UNSIGNED_BYTE,texture->pixels);
532 if(err) {
533 fprintf(stderr, "gluBuild2DMipmaps(): %s\n", gluErrorString(err));
534 exit(1);
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");
548 if(fp == NULL) {
549 perror("failed to open score file");
550 } else {
551 fscanf(fp, "%d", &display->local_score);
552 fclose(fp);
555 display->global_score = 0;
557 if(SDLNet_Init()==-1)
559 fprintf(stderr, "SDLNet_Init(): %s\n",SDLNet_GetError());
560 exit(1);
562 atexit(SDLNet_Quit);
564 IPaddress addr;
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());
569 } else {
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);
574 } else {
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);
578 } else {
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)
592 return;
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());
598 } else {
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());
603 } else {
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)
623 glPushMatrix();
624 glScalef(.0065,.003,.001);
625 glRotatef(-90,0,1,0);
626 glTranslatef(
627 -player->pos[0]-1000, // XXX hardcoded
628 -player->pos[1]-100,
629 -player->pos[2]-(SEGMENT_COUNT-1)*SEGMENT_LEN/2);
630 cave_model(display, cave, 1);
631 glPopMatrix();
635 // vim600:fdm=syntax:fdn=1: