/* * SPEED - by Shawn Hargreaves, 1999 * * Viewport functions (3d projection, wireframe guide rendering, etc). */ #include #include #include "speed.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define NUM_VIEWS 4 /* desired position of a viewport window */ typedef struct { float pos[4]; /* left, top, right, bottom */ } VIEWPOS[NUM_VIEWS]; /* current status of a viewport window */ typedef struct { float pos[4]; /* left, top, right, bottom */ float vel[4]; /* rate of change of the above */ } VIEWINFO[NUM_VIEWS]; /* viewport positioning macros */ #define OFF_TL {{ -0.1, -0.1, -0.1, -0.1 }} #define OFF_TR {{ 1.1, -0.1, 1.1, -0.1 }} #define OFF_BL {{ -0.1, 1.1, -0.1, 1.1 }} #define OFF_BR {{ 1.1, 1.1, 1.1, 1.1 }} #define QTR_TL {{ 0, 0, 0.5, 0.5 }} #define QTR_TR {{ 0.5, 0, 1.0, 0.5 }} #define QTR_BL {{ 0, 0.5, 0.5, 1.0 }} #define QTR_BR {{ 0.5, 0.5, 1.0, 1.0 }} #define BIG_TL {{ 0, 0, 0.7, 0.7 }} #define BIG_TR {{ 0.3, 0, 1.0, 0.7 }} #define BIG_BL {{ 0, 0.3, 0.7, 1.0 }} #define BIG_BR {{ 0.3, 0.3, 1.0, 1.0 }} #define FULL {{ 0, 0, 1.0, 1.0 }} /* list of viewport window positions */ static VIEWPOS viewpos[] = { { FULL, OFF_TR, OFF_BL, OFF_BR }, /* 1 single */ { OFF_TL, FULL, OFF_BL, OFF_BR }, /* 2 single */ { BIG_TL, BIG_BR, OFF_BL, OFF_BR }, /* 12 multiple */ { OFF_TL, OFF_TR, FULL, OFF_BR }, /* 3 single */ { BIG_TL, OFF_TR, BIG_BR, OFF_BR }, /* 13 multiple */ { OFF_TL, BIG_TR, BIG_BL, OFF_BR }, /* 23 multiple */ { FULL, FULL, OFF_BL, OFF_BR }, /* 12 superimpose */ { OFF_TL, OFF_TR, OFF_BL, FULL, }, /* 4 single */ { BIG_TL, OFF_TR, OFF_BL, BIG_BR }, /* 14 multiple */ { OFF_TL, FULL, FULL, OFF_BR }, /* 23 superimpose */ { OFF_TL, BIG_TL, OFF_BL, BIG_BR }, /* 24 multiple */ { OFF_TL, FULL, OFF_BL, FULL }, /* 24 superimpose */ { QTR_TL, QTR_TR, QTR_BL, OFF_BR }, /* 123 multiple */ { BIG_TL, OFF_TR, OFF_BL, BIG_BR }, /* 14 superimpose */ { QTR_TL, OFF_TR, QTR_BL, QTR_BR }, /* 134 multiple */ { FULL, OFF_TR, FULL, OFF_BR }, /* 13 superimpose */ { OFF_TL, OFF_TR, BIG_TL, BIG_BR }, /* 34 multiple */ { OFF_TL, QTR_TR, BIG_BL, QTR_BR }, /* 234 multiple */ { OFF_TL, OFF_TR, FULL, FULL }, /* 34 superimpose */ { FULL, QTR_TR, OFF_BL, QTR_BR }, /* 124 multiple */ { FULL, FULL, OFF_BL, FULL }, /* 124 superimpose */ { QTR_TL, QTR_TR, QTR_BL, QTR_BR }, /* 1234 multiple */ { FULL, FULL, FULL, OFF_BR }, /* 123 superimpose */ { FULL, OFF_TR, FULL, FULL }, /* 134 superimpose */ { OFF_TL, FULL, FULL, FULL }, /* 234 superimpose */ { FULL, FULL, FULL, FULL }, /* 1234 superimpose */ }; /* current viewport state */ static VIEWINFO viewinfo; static int viewnum; static float view_left, view_top, view_right, view_bottom; /* returns a scaling factor for 2d graphics effects */ float view_size() { return ((view_right - view_left) + (view_bottom - view_top)) / 2; } /* initialises the view functions */ void init_view() { int i, j; viewnum = 0; for (i=0; i<4; i++) { for (j=0; j<4; j++) { viewinfo[i].pos[j] = 0; viewinfo[i].vel[j] = 0; } } } /* closes down the view module */ void shutdown_view() { } /* advances to the next view position */ int advance_view() { int cycled = FALSE; viewnum++; if (viewnum >= (int)(sizeof(viewpos)/sizeof(VIEWPOS))) { viewnum = 0; cycled = TRUE; } return cycled; } /* updates the view position */ void update_view() { float delta, vel; int i, j; for (i=0; i<4; i++) { for (j=0; j<4; j++) { delta = viewpos[viewnum][i].pos[j] - viewinfo[i].pos[j]; vel = viewinfo[i].vel[j]; vel *= 0.9; delta = log(ABS(delta)+1.0) * SGN(delta) / 64.0; vel += delta; if ((ABS(delta) < 0.00001) && (ABS(vel) < 0.00001)) { viewinfo[i].pos[j] = viewpos[viewnum][i].pos[j]; viewinfo[i].vel[j] = 0; } else { viewinfo[i].pos[j] += vel; viewinfo[i].vel[j] = vel; } } } } /* flat projection function */ static int project_flat(float *f, int *i, int c) { while (c > 0) { i[0] = view_left + f[0] * (view_right - view_left); i[1] = view_top + f[1] * (view_bottom - view_top); f += 2; i += 2; c -= 2; } return TRUE; } /* spherical coordinate projection function */ static int project_spherical(float *f, int *i, int c) { while (c > 0) { float ang = f[0] * M_PI * 2.0; float xsize = view_right - view_left; float ysize = view_bottom - view_top; float size = MIN(xsize, ysize) / 2.0; float ff = (f[1] > 0.99) ? 0 : (1.0 - f[1] * 0.9); float dx = cos(ang) * ff * size; float dy = sin(ang) * ff * size; i[0] = dx + (view_left + view_right) / 2.0; i[1] = dy + (view_top + view_bottom) / 2.0; f += 2; i += 2; c -= 2; } return TRUE; } /* inside of tube projection function */ static int project_tube(float *f, int *i, int c) { while (c > 0) { float ang = f[0] * M_PI * 2.0 + M_PI / 2.0; float xsize = view_right - view_left; float ysize = view_bottom - view_top; float size = MIN(xsize, ysize) / 2.0; float x = cos(ang); float y = sin(ang); float z = 1.0 + (1.0 - f[1]) * 8.0; i[0] = x/z * size + (view_left + view_right) / 2.0; i[1] = y/z * size + (view_top + view_bottom) / 2.0; f += 2; i += 2; c -= 2; } return TRUE; } /* outside of cylinder projection function */ static int project_cylinder(float *f, int *i, int c) { static MATRIX_f mtx; static int virgin = TRUE; if (virgin) { MATRIX_f m1, m2; get_z_rotate_matrix_f(&m1, -64); qtranslate_matrix_f(&m1, 0, 1.75, 0); get_scaling_matrix_f(&m2, 2.0, 1.0, 1.0); matrix_mul_f(&m1, &m2, &mtx); virgin = FALSE; } while (c > 0) { float ang = (f[0] - player_pos()) * M_PI * 2.0; float xsize = view_right - view_left; float ysize = view_bottom - view_top; float size = MIN(xsize, ysize) / 2.0; float x = cos(ang); float y = sin(ang); float z = 1.0 + (1.0 - f[1]) * 4.0; float xout, yout, zout; apply_matrix_f(&mtx, x, y, z, &xout, &yout, &zout); if (yout > 1.5) return FALSE; i[0] = xout/zout * size + (view_left + view_right) / 2.0; i[1] = (yout/zout * 2 - 1) * size + (view_top + view_bottom) / 2.0; f += 2; i += 2; c -= 2; } return TRUE; } /* draws the entire view */ void draw_view(BITMAP *bmp) { int (*project)(float *f, int *i, int c); int r, g, b, c; int i, n, x, y; float point[6]; int ipoint[6]; clear(bmp); drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0); for (i=0; i<4; i++) { view_left = viewinfo[i].pos[0] * SCREEN_W; view_top = viewinfo[i].pos[1] * SCREEN_H; view_right = viewinfo[i].pos[2] * SCREEN_W; view_bottom = viewinfo[i].pos[3] * SCREEN_H; if ((view_right > view_left) && (view_bottom > view_top) && (view_right > 0) && (view_bottom > 0) && (view_left < SCREEN_W) && (view_top < SCREEN_H)) { switch (i) { case 0: /* flat projection, green */ project = project_flat; r = 0; g = 255; b = 0; break; case 1: /* spherical coordinates, yellow */ project = project_spherical; r = 255; g = 255; b = 0; break; case 2: /* inside a tube, blue */ project = project_tube; r = 0; g = 0; b = 255; break; case 3: /* surface of cylinder, red */ project = project_cylinder; r = 255; g = 0; b = 0; break; default: /* oops! */ ASSERT(FALSE); return; } if (!no_grid) { c = makecol(r/5, g/5, b/5); n = (low_detail) ? 8 : 16; for (x=0; x<=n; x++) { for (y=0; y<=n; y++) { point[0] = (float)x / n; point[1] = (float)y / n; point[2] = (float)(x+1) / n; point[3] = (float)y / n; point[4] = (float)x / n; point[5] = (float)(y+1) / n; if (project(point, ipoint, 6)) { if (x < n) line(bmp, ipoint[0], ipoint[1], ipoint[2], ipoint[3], c); if ((y < n) && ((x < n) || (i == 0))) line(bmp, ipoint[0], ipoint[1], ipoint[4], ipoint[5], c); } } } } draw_player(bmp, r, g, b, project); draw_badguys(bmp, r, g, b, project); draw_bullets(bmp, r, g, b, project); draw_explode(bmp, r, g, b, project); } } solid_mode(); draw_message(bmp); textprintf(bmp, font, 4, 4, makecol(128, 128, 128), "Lives: %d", lives); textprintf(bmp, font, 4, 16, makecol(128, 128, 128), "Score: %d", score); textprintf(bmp, font, 4, 28, makecol(128, 128, 128), "Hiscore: %d", get_hiscore()); blit(bmp, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); }