/** * @brief Prepares the rendering for the special effects. * * Should be called at the beginning of the rendering loop. * * @param dt Current delta tick. * @param real_dt Real delta tick. */ void spfx_begin( const double dt, const double real_dt ) { double ddt; /* Defaults. */ shake_set = 0; if (shake_off) return; #if SDL_VERSION_ATLEAST(1,3,0) /* Decrement the haptic timer. */ if (haptic_lastUpdate > 0.) haptic_lastUpdate -= real_dt; /* Based on real delta-tick. */ #else /* SDL_VERSION_ATLEAST(1,3,0) */ (void) real_dt; /* Avoid warning. */ #endif /* SDL_VERSION_ATLEAST(1,3,0) */ /* Micro basic simple control loop. */ if (dt > shake_fps_min) { ddt = dt; while (ddt > shake_fps_min) { spfx_updateShake( shake_fps_min ); ddt -= shake_fps_min; } spfx_updateShake( ddt ); /* Leftover. */ } else spfx_updateShake( dt ); /* set the new viewport */ gl_matrixTranslate( shake_pos.x, shake_pos.y ); shake_set = 1; }
/** * @brief Starts the rendering engine. */ static void gl_fontRenderStart( const glFontStash* font, double x, double y, const glColour *c ) { double a; /* Enable textures. */ glEnable(GL_TEXTURE_2D); /* Set up matrix. */ gl_matrixMode(GL_MODELVIEW); gl_matrixPush(); gl_matrixTranslate( round(x), round(y) ); /* Handle colour. */ if (font_restoreLast) { a = (c==NULL) ? 1. : c->a; ACOLOUR(*font_lastCol,a); } else { if (c==NULL) glColor4d( 1., 1., 1., 1. ); else COLOUR(*c); } font_restoreLast = 0; /* Activate the appropriate VBOs. */ gl_vboActivateOffset( font->vbo_tex, GL_TEXTURE_COORD_ARRAY, 0, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( font->vbo_vert, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 ); }
/** * @brief Renders a character. */ static int gl_fontRenderGlyph( glFontStash* stsh, uint32_t ch, const glColour *c, int state ) { GLushort ind[6]; double a; const glColour *col; int vbo_id; /* Handle escape sequences. */ if (ch == '\a') {/* Start sequence. */ return 1; } if (state == 1) { col = gl_fontGetColour( ch ); a = (c==NULL) ? 1. : c->a; if (col == NULL) { if (c==NULL) glColor4d( 1., 1., 1., 1. ); else COLOUR(*c); } else ACOLOUR(*col,a); font_lastCol = col; return 0; } /* Unicode goes here. * First try to find the glyph. */ glFontGlyph *glyph; glyph = gl_fontGetGlyph( stsh, ch ); if (glyph == NULL) { WARN(_("Unable to find glyph '%d'!"), ch ); return -1; } /* Activate texture. */ glBindTexture(GL_TEXTURE_2D, glyph->tex->id); /* VBO indices. */ vbo_id = glyph->vbo_id; ind[0] = vbo_id + 0; ind[1] = vbo_id + 1; ind[2] = vbo_id + 3; ind[3] = vbo_id + 1; ind[4] = vbo_id + 3; ind[5] = vbo_id + 2; /* Draw the element. */ glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, ind ); /* Translate matrix. */ gl_matrixTranslate( glyph->adv_x, glyph->adv_y ); return 0; }
/** * @brief Sets the opengl viewport. */ void gl_viewport( int x, int y, int w, int h ) { gl_matrixMode(GL_PROJECTION); gl_matrixIdentity(); gl_matrixOrtho( 0., /* Left edge. */ gl_screen.nw, /* Right edge. */ 0., /* Bottom edge. */ gl_screen.nh, /* Top edge. */ -1., /* near */ 1. ); /* far */ /* Take into account possible translation. */ gl_screen.x = x; gl_screen.y = y; gl_matrixTranslate( x, y ); /* Set screen size. */ gl_screen.w = w; gl_screen.h = h; /* Take into account possible scaling. */ if (gl_screen.scale != 1.) gl_matrixScale( gl_screen.wscale, gl_screen.hscale ); }
/** * @brief Renders the nebula overlay (hides what player can't see). * * @param dt Current delta tick. */ void nebu_renderOverlay( const double dt ) { (void) dt; double gx, gy; double ox, oy; double z; double sx, sy; /* Get GUI offsets. */ gui_getOffset( &gx, &gy ); /* Get zoom. */ z = cam_getZoom(); /* * Renders the puffs */ nebu_renderPuffs( 0 ); /* Prepare the matrix */ ox = gx; oy = gy; spfx_getShake( &sx, &sy ); ox += sx; oy += sy; gl_matrixPush(); gl_matrixTranslate( SCREEN_W/2.+ox, SCREEN_H/2.+oy ); gl_matrixScale( z, z ); /* * Mask for area player can still see (partially) */ glShadeModel(GL_SMOOTH); gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( nebu_vboOverlay, GL_COLOR_ARRAY, sizeof(GLfloat)*2*18, 4, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 18 ); /* * Solid nebula for areas the player can't see */ glShadeModel(GL_FLAT); /* Colour is shared. */ gl_vboActivateOffset( nebu_vboOverlay, GL_COLOR_ARRAY, sizeof(GLfloat)*((2+4)*18 + 2*28), 4, GL_FLOAT, 0 ); /* Top left. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 0*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Top right. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 1*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Bottom right. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 2*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Bottom left. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 3*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); gl_vboDeactivate(); gl_matrixPop(); /* Reset puff movement. */ puff_x = 0.; puff_y = 0.; gl_checkErr(); }
/** * @brief Renders the nebula using the multitexture approach. * * @param dt Current delta tick. */ static void nebu_renderMultitexture( const double dt ) { GLfloat col[4]; int temp; double sx, sy; /* calculate frame to draw */ nebu_timer -= dt; if (nebu_timer < 0.) { /* Time to change. */ temp = cur_nebu[0] - cur_nebu[1]; cur_nebu[1] = cur_nebu[0]; cur_nebu[0] += temp; if (cur_nebu[0] >= NEBULA_Z) cur_nebu[0] = cur_nebu[1] - 1; else if (cur_nebu[0] < 0) cur_nebu[0] = cur_nebu[1] + 1; /* Change timer. */ nebu_timer += nebu_dt; /* Case it hasn't rendered in a while so it doesn't go crazy. */ if (nebu_timer < 0) nebu_timer = nebu_dt; } /* Set the colour */ col[0] = cBlue.r; col[1] = cBlue.g; col[2] = cBlue.b; col[3] = (nebu_dt - nebu_timer) / nebu_dt; /* Set up the targets */ /* Texture 0 */ nglActiveTexture( GL_TEXTURE0 ); glEnable(GL_TEXTURE_2D); glBindTexture( GL_TEXTURE_2D, nebu_textures[cur_nebu[1]]); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); /* Texture 1 */ nglActiveTexture( GL_TEXTURE1 ); glEnable(GL_TEXTURE_2D); glBindTexture( GL_TEXTURE_2D, nebu_textures[cur_nebu[0]]); /* Prepare it */ glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_INTERPOLATE ); /* Colour */ glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, col ); /* Arguments */ /* Arg0 */ glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); /* Arg1 */ glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA ); /* Arg2 */ glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_ALPHA, GL_CONSTANT ); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA ); /* Compensate possible rumble */ spfx_getShake( &sx, &sy ); gl_matrixPush(); gl_matrixTranslate( -sx, -sy ); /* Now render! */ gl_vboActivateOffset( nebu_vboBG, GL_VERTEX_ARRAY, sizeof(GL_FLOAT) * 0*2*4, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( nebu_vboBG, GL_TEXTURE0, sizeof(GL_FLOAT) * 1*2*4, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( nebu_vboBG, GL_TEXTURE1, sizeof(GL_FLOAT) * 2*2*4, 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 ); gl_vboDeactivate(); gl_matrixPop(); /* Set values to defaults */ glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glDisable(GL_TEXTURE_2D); nglActiveTexture( GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glDisable(GL_TEXTURE_2D); /* Anything failed? */ gl_checkErr(); }
/** * @brief Renders the starry background. * * @param dt Current delta tick. */ void background_renderStars( const double dt ) { (void) dt; unsigned int i; GLfloat hh, hw, h, w; GLfloat x, y, m, b; GLfloat brightness; double z; double sx, sy; int shade_mode; int j, n; /* * gprof claims it's the slowest thing in the game! */ /* Do some scaling for now. */ z = cam_getZoom(); z = 1. * (1. - conf.zoom_stars) + z * conf.zoom_stars; gl_matrixPush(); gl_matrixTranslate( SCREEN_W/2., SCREEN_H/2. ); gl_matrixScale( z, z ); if (!paused && (player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING)) { /* update position */ /* Calculate some dimensions. */ w = (SCREEN_W + 2.*STAR_BUF); w += conf.zoom_stars * (w / conf.zoom_far - 1.); h = (SCREEN_H + 2.*STAR_BUF); h += conf.zoom_stars * (h / conf.zoom_far - 1.); hw = w/2.; hh = h/2.; if ((star_x > SCREEN_W) || (star_y > SCREEN_H)) { sx = ceil( star_x / SCREEN_W ); sy = ceil( star_y / SCREEN_H ); n = MAX( sx, sy ); star_x /= (double)n; star_y /= (double)n; } else n = 1; /* Calculate new star positions. */ for (j=0; j < n; j++) { for (i=0; i < nstars; i++) { /* calculate new position */ b = 1./(9. - 10.*star_colour[8*i+3]); star_vertex[4*i+0] = star_vertex[4*i+0] + star_x*b; star_vertex[4*i+1] = star_vertex[4*i+1] + star_y*b; /* check boundaries */ if (star_vertex[4*i+0] > hw) star_vertex[4*i+0] -= w; else if (star_vertex[4*i+0] < -hw) star_vertex[4*i+0] += w; if (star_vertex[4*i+1] > hh) star_vertex[4*i+1] -= h; else if (star_vertex[4*i+1] < -hh) star_vertex[4*i+1] += h; } } /* Upload the data. */ gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex ); } /* Decide on shade mode. */ shade_mode = 0; if ((player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING)) { if (pilot_isFlag(player.p,PILOT_HYPERSPACE) && /* hyperspace fancy effects */ (player.p->ptimer < HYPERSPACE_STARS_BLUR)) { glShadeModel(GL_SMOOTH); shade_mode = 1; /* lines will be based on velocity */ m = HYPERSPACE_STARS_BLUR-player.p->ptimer; m /= HYPERSPACE_STARS_BLUR; m *= HYPERSPACE_STARS_LENGTH; x = m*cos(VANGLE(player.p->solid->vel)); y = m*sin(VANGLE(player.p->solid->vel)); } else if (dt_mod > 3.) { glShadeModel(GL_SMOOTH); shade_mode = 1; /* lines will be based on velocity */ m = (dt_mod-3.)*VMOD(player.p->solid->vel)/10.; x = m*cos(VANGLE(player.p->solid->vel)); y = m*sin(VANGLE(player.p->solid->vel)); } if (shade_mode) { /* Generate lines. */ for (i=0; i < nstars; i++) { brightness = star_colour[8*i+3]; star_vertex[4*i+2] = star_vertex[4*i+0] + x*brightness; star_vertex[4*i+3] = star_vertex[4*i+1] + y*brightness; } /* Upload new data. */ gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex ); } } /* Render. */ gl_vboActivate( star_vertexVBO, GL_VERTEX_ARRAY, 2, GL_FLOAT, 2 * sizeof(GLfloat) ); gl_vboActivate( star_colourVBO, GL_COLOR_ARRAY, 4, GL_FLOAT, 4 * sizeof(GLfloat) ); if (shade_mode) { glDrawArrays( GL_LINES, 0, nstars ); glDrawArrays( GL_POINTS, 0, nstars ); /* This second pass is when the lines are very short that they "lose" intensity. */ glShadeModel(GL_FLAT); } else { glDrawArrays( GL_POINTS, 0, nstars ); } /* Clear star movement. */ star_x = 0.; star_y = 0.; /* Disable vertex array. */ gl_vboDeactivate(); /* Pop matrix. */ gl_matrixPop(); /* Check for errors. */ gl_checkErr(); }
/** * @brief Renders the overlay map. * * @param dt Current delta tick. */ void ovr_render( double dt ) { (void) dt; int i, j; Pilot **pstk; int n; double w, h, res; double x,y; glColour c = { .r=0., .g=0., .b=0., .a=0.5 }; /* Must be open. */ if (!ovr_open) return; /* Player must be alive. */ if (player_isFlag( PLAYER_DESTROYED ) || (player.p == NULL)) return; /* Default values. */ w = SCREEN_W; h = SCREEN_H; res = ovr_res; /* First render the background overlay. */ gl_renderRect( 0., 0., w, h, &c ); /* We need to center in the image first. */ gl_matrixPush(); gl_matrixTranslate( w/2., h/2. ); /* Render planets. */ for (i=0; i<cur_system->nplanets; i++) if ((cur_system->planets[ i ]->real == ASSET_REAL) && (i != player.p->nav_planet)) gui_renderPlanet( i, RADAR_RECT, w, h, res, 1 ); if (player.p->nav_planet > -1) gui_renderPlanet( player.p->nav_planet, RADAR_RECT, w, h, res, 1 ); /* Render jump points. */ for (i=0; i<cur_system->njumps; i++) if (i != player.p->nav_hyperspace) gui_renderJumpPoint( i, RADAR_RECT, w, h, res, 1 ); if (player.p->nav_hyperspace > -1) gui_renderJumpPoint( player.p->nav_hyperspace, RADAR_RECT, w, h, res, 1 ); /* Render pilots. */ pstk = pilot_getAll( &n ); j = 0; for (i=0; i<n; i++) { if (pstk[i]->id == PLAYER_ID) /* Skip player. */ continue; if (pstk[i]->id == player.p->target) j = i; else gui_renderPilot( pstk[i], RADAR_RECT, w, h, res, 1 ); } /* Render the targetted pilot */ if (j!=0) gui_renderPilot( pstk[j], RADAR_RECT, w, h, res, 1 ); /* Check if player has goto target. */ if (player_isFlag(PLAYER_AUTONAV) && (player.autonav == AUTONAV_POS_APPROACH)) { x = player.autonav_pos.x / res; y = player.autonav_pos.y / res; gl_renderCross( x, y, 5., &cRadar_hilight ); gl_printRaw( &gl_smallFont, x+10., y-gl_smallFont.h/2., &cRadar_hilight, "GOTO" ); } /* Render the player. */ gui_renderPlayer( res, 1 ); /* Render markers. */ ovr_mrkRenderAll( res ); /* Pop the matrix. */ gl_matrixPop(); } /** * @brief Renders all the markers. * * @param res Resolution to render at. */ static void ovr_mrkRenderAll( double res ) { int i; ovr_marker_t *mrk; double x, y; if (ovr_markers == NULL) return; for (i=0; i<array_size(ovr_markers); i++) { mrk = &ovr_markers[i]; x = mrk->u.pt.x / res; y = mrk->u.pt.y / res; gl_renderCross( x, y, 5., &cRadar_hilight ); if (mrk->text != NULL) gl_printRaw( &gl_smallFont, x+10., y-gl_smallFont.h/2., &cRadar_hilight, mrk->text ); } }