diff options
Diffstat (limited to 'src/video/gamegraphics.c')
-rw-r--r-- | src/video/gamegraphics.c | 480 |
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); +} |