more cave wall irregularities
[cave9.git] / src / display.cpp
blob4dc6435dd41f3d4f4704dfeaae32052a76819883
1 /*
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>
20 #include <GL/gl.h>
21 #include <GL/glu.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include <float.h>
26 #include <assert.h>
27 #include "display.h"
29 void viewport(Display* display, GLsizei w, GLsizei h, GLsizei bpp,
30 bool fullscreen, int aa=0)
32 // video mode
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 );
39 if(aa) {
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;
45 if(fullscreen)
46 flags |= SDL_FULLSCREEN;
47 display->screen = SDL_SetVideoMode(w, h, bpp, flags);
48 if(display->screen == NULL)
49 goto error;
51 bpp = display->screen->format->BitsPerPixel;
52 //printf("%dx%dx%d\n", display->screen->w, display->screen->h, display->screen->format->BitsPerPixel);
54 #if 0
55 if(aa) {
56 int arg;
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);
62 #endif
64 SDL_WM_SetCaption("cave9 -- 9hells.org", "cave9");
65 SDL_ShowCursor(SDL_DISABLE);
67 // projection
68 glViewport(0,0,w,h);
70 glMatrixMode(GL_PROJECTION);
71 glLoadIdentity();
72 gluPerspective(45,-w/(GLfloat)h,display->near_plane,display->far_plane);
75 // settings
76 glClearColor(0,0,0,0);
77 glClearDepth(1);
79 glDepthFunc(GL_LEQUAL);
80 glEnable(GL_DEPTH_TEST);
82 glPolygonMode( GL_FRONT, GL_FILL );
83 glEnable(GL_CULL_FACE);
84 glCullFace(GL_FRONT);
86 if(aa) {
87 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
88 glShadeModel(GL_SMOOTH);
89 glEnable(GL_LINE_SMOOTH);
90 } else {
91 glShadeModel(GL_FLAT);
92 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
95 glLineWidth(16);
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);
105 glEnable(GL_FOG);
108 if(aa) {
109 #ifdef GL_ARB_multisample
110 glEnable(GL_MULTISAMPLE_ARB);
111 #endif
112 glHint(GL_MULTISAMPLE_FILTER_HINT_NV,GL_NICEST);
115 return;
116 error:
117 fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
118 exit(1);
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;
127 gluLookAt(
128 display->cam[0], display->cam[1], display->cam[2],
129 display->target[0], display->target[1], display->target[2],
130 0,1,0
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 ) {
140 int id = wire ?
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;
147 if(!wire)
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;
154 if(!wire) {
155 if(i0==0||i1==0||k==3*SECTOR_COUNT/4)
156 glColor4f(1, 0, 0, 0.5);
157 else
158 glColor4f(1, 1, 1, 0.5);
161 if(!wire) {
162 glTexCoord2f(
163 (float)(cave->i+i)/SEGMENT_COUNT,
164 (float)k/SECTOR_COUNT);
165 } else {
166 glColor4f(
167 (float)i0/SEGMENT_COUNT,
168 1-(float)i0/SEGMENT_COUNT,
169 (float)k0/SECTOR_COUNT,
170 0.5);
172 glVertex3fv(cave->segs[i0][k0]);
174 if(!wire) {
175 glTexCoord2f(
176 ((float)(cave->i+i+1))/SEGMENT_COUNT,
177 (float)k/SECTOR_COUNT);
178 } else {
179 glColor4f(
180 (float)i1/SEGMENT_COUNT,
181 1-(float)i1/SEGMENT_COUNT,
182 (float)k0/SECTOR_COUNT,
183 0.5);
185 glVertex3fv(cave->segs[i1][k0]);
187 glEnd();
189 glEndList();
192 if(wire) {
193 glDisable(GL_DEPTH_TEST);
194 glEnable(GL_BLEND);
195 glDisable(GL_TEXTURE_2D);
196 } else {
197 glEnable(GL_DEPTH_TEST);
198 glDisable(GL_BLEND);
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)
210 return;
212 glColor3f(.2,.2,.2);
214 float w = MONOLITH_WIDTH/2;
215 float h = MONOLITH_HEIGHT/2;
216 float d = MONOLITH_DEPTH;
218 glEnable(GL_DEPTH_TEST);
219 glDisable(GL_BLEND);
220 glDisable(GL_TEXTURE_2D);
222 glPushMatrix();
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 );
232 glEnd();
234 glBegin( GL_QUADS );
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 );
240 glEnd();
242 glPopMatrix();
245 void ship_model(Display* display, Ship* ship)
247 if(!display->cockpit)
248 return;
250 if(ship->dist <= 0)
251 return;
253 float alpha = (1-MIN(1,(ship->pos[2]/MIN_CAVE_RADIUS_DEPTH)))/8.;
254 if(alpha == 0)
255 return;
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;
261 float f =1.8;
263 glDisable(GL_DEPTH_TEST);
264 glEnable(GL_BLEND);
265 glDisable(GL_TEXTURE_2D);
267 glColor4f(1,white,white,alpha);
268 glPushMatrix();
269 glTranslatef(0,0,-SHIP_RADIUS*f);
270 glCallList( display->ship_list );
271 glPopMatrix();
273 glPushMatrix();
274 display_world_transform(display, ship);
275 glTranslatef(
276 ship->pos[0],
277 ship->pos[1],
278 ship->pos[2]+SHIP_RADIUS*f
280 glCallList( display->ship_list );
281 glPopMatrix();
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')
289 return;
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);
295 glEnable(GL_BLEND);
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);
305 glPushMatrix();
306 glColor3f(r,g,b);
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);
314 glEnd();
315 glPopMatrix();
318 void display_hud(Display* display, Ship* player)
320 if(player->dist == FLT_MAX)
321 return;
323 float max_vel[3] = { MAX_VEL_X, MAX_VEL_Y, MAX_VEL_Z };
324 float vel = MIN(1,
325 log(1+LEN(player->vel)-MAX_VEL_Z) /
326 log(1+LEN(max_vel)-MAX_VEL_Z));
327 char gauge[11];
328 int i = int(vel*10);
329 memset(gauge,'/',i);
330 gauge[i] = '\0';
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",
338 gauge, score
340 } else {
341 if(score > display->session_score)
342 display->session_score = score;
343 if(player->start) {
344 snprintf(buf, HUD_TEXT_MAX, "velocity %s score %d (%d) - %d",
345 gauge, score,
346 display->session_score,
347 (int)player->start
349 } else {
350 if(score > display->local_score) {
351 display->local_score = score;
352 FILE* fp = fopen(SCORE_FILE, "w");
353 if(fp == NULL) {
354 perror("failed to open score file");
355 } else {
356 fprintf(fp, "%d", display->local_score);
357 fclose(fp);
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)",
365 gauge, score,
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);
390 glLoadIdentity();
393 void display_end_frame(Display* display)
395 glFinish();
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
407 glPushMatrix();
408 display_world_transform(display, player);
409 cave_model(display, cave, 0);
410 monolith_model(display, cave, player);
411 glPopMatrix();
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]);
457 glEnd();
459 glEndList();
461 return ship_list;
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());
471 exit(1);
473 atexit(SDL_Quit);
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);
480 int w = args->width;
481 int h = args->height;
482 int f = args->fullscreen;
483 if(args->highres) {
484 #if SDL_VERSION_ATLEAST(1,2,11)
485 const SDL_VideoInfo* info = SDL_GetVideoInfo();
486 assert(info != NULL);
487 w = info->current_w;
488 h = info->current_h;
489 #else
490 w = 1024;
491 h = 768;
492 #endif
493 f = 1;
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());
502 exit(1);
504 atexit(TTF_Quit);
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());
511 exit(1);
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());
542 exit(1);
545 GLenum err = gluBuild2DMipmaps(GL_TEXTURE_2D,
546 GL_RGB, texture->w, texture->h,
547 GL_RGB, GL_UNSIGNED_BYTE,texture->pixels);
548 if(err) {
549 fprintf(stderr, "gluBuild2DMipmaps(): %s\n", gluErrorString(err));
550 exit(1);
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");
564 if(fp == NULL) {
565 perror("failed to open score file");
566 } else {
567 fscanf(fp, "%d", &display->local_score);
568 fclose(fp);
571 display->global_score = 0;
573 if(SDLNet_Init()==-1)
575 fprintf(stderr, "SDLNet_Init(): %s\n",SDLNet_GetError());
576 exit(1);
578 atexit(SDLNet_Quit);
580 IPaddress addr;
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());
585 } else {
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);
590 } else {
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);
594 } else {
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)
608 return;
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());
614 } else {
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());
619 } else {
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)
639 glPushMatrix();
640 glScalef(.0065,.003,.001);
641 glRotatef(-90,0,1,0);
642 glTranslatef(
643 -player->pos[0]-1000, // XXX hardcoded
644 -player->pos[1]-100,
645 -player->pos[2]-(SEGMENT_COUNT-1)*SEGMENT_LEN/2);
646 cave_model(display, cave, 1);
647 glPopMatrix();
651 // vim600:fdm=syntax:fdn=1: