summaryrefslogtreecommitdiffstats
path: root/src/video/gamegraphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/video/gamegraphics.c')
-rw-r--r--src/video/gamegraphics.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/video/gamegraphics.c b/src/video/gamegraphics.c
new file mode 100644
index 0000000..d2108c2
--- /dev/null
+++ b/src/video/gamegraphics.c
@@ -0,0 +1,480 @@
+#include "video/video.h"
+#include "game/game.h"
+
+#include "video/skybox.h"
+#include "video/recognizer.h"
+#include "video/explosion.h"
+
+// static float arena[] = { 1.0, 1.2, 1, 0.0 };
+
+#define MAX_LOD_LEVEL 3
+static int lod_dist[MAX_LOD_LEVEL + 1][LC_LOD + 1] = {
+ { 1000, 1000, 1000 }, /* insane */
+ { 100, 200, 400 }, /* high */
+ { 30, 100, 200 }, /* low */
+ { 10, 30, 150 } /* ugly */
+};
+
+/* spoke colors */
+static float SpokeColor[4] = {1.0, 1.0, 1.0, 1.0};
+static float NoSpokeColor[4] = {0.0, 0.0, 0.0, 1.0};
+
+void drawGame(void) {
+ GLint i;
+
+ polycount = 0;
+
+ glEnable(GL_DEPTH_TEST);
+
+ glClearColor(gSettingsCache.clear_color[0],
+ gSettingsCache.clear_color[1],
+ gSettingsCache.clear_color[2],
+ gSettingsCache.clear_color[3]);
+
+ if(gSettingsCache.use_stencil) {
+ glClearStencil(0);
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ } else {
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ }
+
+ for(i = 0; i < vp_max[gViewportType]; i++) {
+ Player *p = game->player + viewport_content[i];
+ PlayerVisual *pV = gPlayerVisuals + viewport_content[i];
+ Visual *d = & pV->display;
+
+ if(d->onScreen == 1) {
+ glViewport(d->vp_x, d->vp_y, d->vp_w, d->vp_h);
+ drawCam(p, pV);
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+ if (gSettingsCache.show_scores)
+ drawScore(p, d);
+ if (gSettingsCache.show_ai_status)
+ if(p->ai->active == AI_COMPUTER)
+ drawAI(d);
+ }
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ if (gSettingsCache.show_fps)
+ drawFPS(gScreen);
+
+ if(gSettingsCache.show_console)
+ drawConsole(gScreen);
+
+ /* printf("%d polys\n", polycount); */
+}
+
+float GetDistance(float *v, float *p, float *d) {
+ float diff[3];
+ float tmp[3];
+ float t;
+ vsub(v, p, diff);
+ t = scalarprod(d, diff) / scalarprod(d, d);
+ vcopy(d, tmp);
+ vmul(tmp, t);
+ vsub(diff, tmp, tmp);
+ return sqrtf( scalarprod(tmp, tmp) );
+}
+
+static float dirangles[] = { 0, -90, -180, 90, 180, -270 };
+
+float getDirAngle(int time, Player *p) {
+ int last_dir;
+ float dirAngle;
+
+ if(time < TURN_LENGTH) {
+ last_dir = p->data->last_dir;
+ if(p->data->dir == 3 && last_dir == 2)
+ last_dir = 4;
+ if(p->data->dir == 2 && last_dir == 3)
+ last_dir = 5;
+ dirAngle = ((TURN_LENGTH - time) * dirangles[last_dir] +
+ time * dirangles[p->data->dir]) / TURN_LENGTH;
+ } else
+ dirAngle = dirangles[p->data->dir];
+
+ return dirAngle;
+}
+
+void doCycleTurnRotation(PlayerVisual *pV, Player *p) {
+ int neigung_dir = -1;
+ int time = game2->time.current - p->data->turn_time;
+ float dirAngle = getDirAngle(time, p);
+
+ glRotatef(dirAngle, 0, 0, 1);
+
+#define neigung 25
+ if(time < TURN_LENGTH && p->data->last_dir != p->data->dir) {
+ float axis = 1.0f;
+ if(p->data->dir < p->data->last_dir && p->data->last_dir != 3)
+ axis = -1.0;
+ else if((p->data->last_dir == 3 && p->data->dir == 2) ||
+ (p->data->last_dir == 0 && p->data->dir == 3))
+ axis = -1.0;
+ glRotated(neigung * sin(PI * time / TURN_LENGTH),
+ 0.0, axis * neigung_dir, 0.0);
+ }
+#undef neigung
+}
+
+void drawCycleShadow(PlayerVisual *pV, Player *p, int lod, int drawTurn) {
+ Mesh *cycle;
+ int turn_time = game2->time.current - p->data->turn_time;
+
+ if(turn_time < TURN_LENGTH && !drawTurn)
+ return;
+
+ if(pV->exp_radius != 0)
+ return;
+
+ cycle = lightcycle[lod];
+
+ /* states */
+
+ glEnable(GL_CULL_FACE);
+
+ if(gSettingsCache.use_stencil) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glStencilFunc(GL_GREATER, 1, 1);
+ glEnable(GL_BLEND);
+ glColor4fv(shadow_color);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glColor3f(0, 0, 0);
+ glDisable(GL_BLEND);
+ }
+
+ /* transformations */
+
+ glPushMatrix();
+ {
+ float x, y;
+ getPositionFromData(&x, &y, p->data);
+ glTranslatef(x,y, 0.0);
+ }
+ glMultMatrixf(shadow_matrix);
+ if (gSettingsCache.turn_cycle) {
+ doCycleTurnRotation(pV, p);
+ } else if (pV->exp_radius == 0) {
+ glRotatef(dirangles[p->data->dir], 0.0, 0.0, 1.0);
+ }
+ glTranslatef(0, 0, cycle->BBox.vSize.v[2] / 2);
+
+ /* render */
+
+ drawModel(cycle, TRI_MESH);
+
+ /* restore */
+
+ if(gSettingsCache.use_stencil)
+ glDisable(GL_STENCIL_TEST);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glPopMatrix();
+}
+
+void drawCycle(Player *p, PlayerVisual *pV, int lod, int drawTurn) {
+ Mesh *cycle = lightcycle[lod];
+
+ unsigned int spoke_time = game2->time.current - pV->spoke_time;
+ int turn_time = game2->time.current - p->data->turn_time;
+
+ if(turn_time < TURN_LENGTH && !drawTurn)
+ return;
+
+ glPushMatrix();
+ {
+ float x, y;
+ getPositionFromData(&x, &y, p->data);
+ glTranslatef(x, y, 0.0);
+ }
+
+ if (pV->exp_radius == 0 && gSettingsCache.turn_cycle == 0) {
+ glRotatef(dirangles[p->data->dir], 0.0, 0.0, 1.0);
+ }
+
+ if (gSettingsCache.turn_cycle) {
+ doCycleTurnRotation(pV, p);
+ }
+
+ setupLights(eCycles);
+
+ SetMaterialColor(cycle, "Hull", eDiffuse, pV->pColorDiffuse);
+ SetMaterialColor(cycle, "Hull", eSpecular, pV->pColorSpecular);
+
+ if (gSettingsCache.light_cycles) {
+ glEnable(GL_LIGHTING);
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_TRUE);
+
+ if (pV->exp_radius == 0) {
+ glEnable(GL_NORMALIZE);
+
+ glTranslatef(0, 0, cycle->BBox.vSize.v[2] / 2);
+
+ /* draw spoke animation */
+ if (spoke_time > 140 - (p->data->speed * 10)
+ && game->pauseflag == PAUSE_GAME_RUNNING) {
+ if (pV->spoke_state == 1) {
+ pV->spoke_state = 0;
+ SetMaterialColor(cycle, "Spoke", eSpecular, SpokeColor);
+ SetMaterialColor(cycle, "Spoke", eAmbient, SpokeColor);
+ } else {
+ pV->spoke_state = 1;
+ SetMaterialColor(cycle, "Spoke", eSpecular, NoSpokeColor);
+ SetMaterialColor(cycle, "Spoke", eAmbient, NoSpokeColor);
+ }
+ pV->spoke_time = game2->time.current;
+ }
+
+ glEnable(GL_CULL_FACE);
+ drawModel(cycle, TRI_MESH);
+ glDisable(GL_CULL_FACE);
+
+ } else if(pV->exp_radius < EXP_RADIUS_MAX) {
+
+ glEnable(GL_BLEND);
+
+ if (gSettingsCache.show_impact) {
+ drawImpact(pV);
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glTranslatef(0, 0, cycle->BBox.vSize.v[2] / 2);
+
+ drawModelExplosion(cycle, pV->exp_radius);
+ }
+ glDisable(GL_BLEND);
+ glDisable(GL_LIGHTING);
+ glPopMatrix();
+}
+
+int playerVisible(Player *eye, Player *target) {
+ float v1[3];
+ float v2[3];
+ float tmp[3];
+ float s;
+ float d;
+ int i;
+ int lod_level;
+ float x, y;
+
+ vsub(eye->camera->target, eye->camera->cam, v1);
+ normalize(v1);
+
+ getPositionFromData(&x, &y, target->data);
+ tmp[0] = x;
+ tmp[1] = y;
+ tmp[2] = 0;
+
+ lod_level = (gSettingsCache.lod > MAX_LOD_LEVEL) ?
+ MAX_LOD_LEVEL : gSettingsCache.lod;
+
+ /* calculate lod */
+ vsub(eye->camera->cam, tmp, v2);
+ d = length(v2);
+ for(i = 0; i < LC_LOD && d >= lod_dist[lod_level][i]; i++);
+ if(i >= LC_LOD)
+ return -1;
+
+ vsub(tmp, eye->camera->cam, v2);
+ normalize(v2);
+ s = scalarprod(v1, v2);
+ /* maybe that's not exactly correct, but I didn't notice anything */
+ d = cosf((gSettingsCache.fov / 2) * 2 * PI / 360.0);
+ /*
+ printf("v1: %.2f %.2f %.2f\nv2: %.2f %.2f %.2f\ns: %.2f d: %.2f\n\n",
+ v1[0], v1[1], v1[2], v2[0], v2[1], v2[2],
+ s, d);
+ */
+ if(s < d-(lightcycle[i]->BBox.fRadius*2))
+ return -1;
+ else
+ return i;
+}
+
+void drawPlayers(Player *p, PlayerVisual *pV) {
+ int i;
+
+ for(i = 0; i < game->players; i++) {
+ int lod;
+ int drawTurn = 1;
+
+ if (gSettingsCache.camType == CAM_TYPE_COCKPIT &&
+ p == &game->player[i])
+ drawTurn = 0;
+
+ lod = playerVisible(p, &(game->player[i]));
+ if (lod >= 0) {
+ drawCycle(game->player + i, gPlayerVisuals + i, lod, drawTurn);
+ }
+ }
+}
+
+void drawCam(Player *p, PlayerVisual* pV) {
+ int i;
+ float up[3] = { 0, 0, 1 };
+ Visual *d = & pV->display;
+
+ glColor3f(0.0, 1.0, 0.0);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ doPerspective(gSettingsCache.fov, d->vp_w / d->vp_h,
+ gSettingsCache.znear, game2->rules.grid_size * 6.5f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ /* set positions for GL lights in world coordinates */
+ glLightfv(GL_LIGHT1, GL_POSITION, p->camera->cam);
+
+ doLookAt(p->camera->cam, p->camera->target, up);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_BLEND);
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+
+ /* skybox */
+ if (gSettingsCache.show_skybox) {
+ drawSkybox(game2->rules.grid_size);
+ }
+
+ /* fixme: clear z-buffer handling */
+ /* glDepthMask(GL_TRUE); */
+
+ /* floor */
+ if (gSettingsCache.show_floor_texture) {
+ drawFloorTextured(game2->rules.grid_size,
+ gScreen->textures[TEX_FLOOR]);
+ } else {
+ /* should this be an artpack setting? */
+ float line_color[] = {1.0, 1.0, 1.0};
+
+ drawFloorGrid(game2->rules.grid_size,
+ gSettingsCache.line_spacing,
+ line_color,
+ gSettingsCache.clear_color);
+ }
+
+ /* glDepthMask(GL_FALSE); */
+
+ /* shadows on the floor: cycle, recognizer, trails */
+ if (gSettingsCache.show_recognizer) {
+ drawRecognizerShadow();
+ }
+
+ for(i = 0; i < game->players; i++) {
+ int lod = playerVisible(p, game->player + i);
+ if (lod >= 0) {
+ int drawTurn = 1;
+ if (! gSettingsCache.camType == CAM_TYPE_COCKPIT ||
+ p != &game->player[i])
+ drawTurn = 0;
+ drawCycleShadow(gPlayerVisuals + i, game->player + i, lod, drawTurn);
+ }
+ if (game->player[i].data->trail_height > 0 )
+ drawTrailShadow(game->player + i, gPlayerVisuals + i);
+ }
+
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+
+ if (gSettingsCache.show_recognizer &&
+ p->data->speed != SPEED_GONE) {
+ drawRecognizer();
+ }
+
+ if (gSettingsCache.show_wall == 1) {
+ drawWalls();
+ }
+
+ drawPlayers(p, pV);
+
+ setupLights(eWorld);
+
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(1,1);
+
+ {
+ TrailMesh mesh;
+ mesh.pVertices = (vec3*) malloc(1000 * sizeof(vec3));
+ mesh.pNormals = (vec3*) malloc(1000 * sizeof(vec3));
+ mesh.pColors = (unsigned char*) malloc(1000 * 4 * sizeof(float));
+ mesh.pTexCoords = (vec2*) malloc(1000 * sizeof(vec2));
+ mesh.pIndices = (unsigned short*) malloc(1000 * 2);
+
+ for(i = 0; i < game->players; i++) {
+ if (game->player[i].data->trail_height > 0 ) {
+ int vOffset = 0;
+ int iOffset = 0;
+ mesh.iUsed = 0;
+ trailGeometry(game->player + i, gPlayerVisuals + i,
+ &mesh, &vOffset, &iOffset);
+ bowGeometry(game->player + i, gPlayerVisuals + i,
+ &mesh, &vOffset, &iOffset);
+ trailStatesNormal(game->player + i, gScreen->textures[TEX_DECAL]);
+ trailRender(&mesh);
+ trailStatesRestore();
+ }
+ }
+ free(mesh.pVertices);
+ free(mesh.pNormals);
+ free(mesh.pColors);
+ free(mesh.pTexCoords);
+ free(mesh.pIndices);
+ }
+
+ glDisable(GL_POLYGON_OFFSET_FILL);
+
+ for(i = 0; i < game->players; i++)
+ if (game->player[i].data->trail_height > 0 )
+ drawTrailLines(game->player + i, gPlayerVisuals + i);
+
+ /* transparent stuff */
+ /* draw the glow around the other players: */
+ if (gSettingsCache.show_glow == 1) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ for (i = 0; i < game->players; i++) {
+ if (p != game->player + i && PLAYER_IS_ACTIVE(game->player + i)) {
+ drawGlow(p->camera, game->player + i, gPlayerVisuals + i,
+ d, TRAIL_HEIGHT * 4);
+ }
+
+ glDisable(GL_BLEND);
+ }
+ }
+ /* 2d hack */
+ if(gSettingsCache.map_ratio_w > 0)
+ {
+ Visual d2d;
+ memcpy(&d2d, d, sizeof(Visual));
+ d2d.vp_w *= gSettingsCache.map_ratio_w;
+ d2d.vp_h *= gSettingsCache.map_ratio_h;
+
+ d2d.vp_x += 20;
+ d2d.vp_y += 20;
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ draw2D(&d2d);
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ }
+}
+
+void initGLGame(void) {
+ glShadeModel( GL_SMOOTH );
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+}