Example #1
0
File: opengl.c Project: Dinth/naev
/**
 * @brief Takes a screenshot.
 *
 *    @param filename Name of the file to save screenshot as.
 */
void gl_screenshot( const char *filename )
{
   GLubyte *screenbuf;
   png_bytep *rows;
   int i, w, h;

   /* Allocate data. */
   w           = gl_screen.rw;
   h           = gl_screen.rh;
   screenbuf   = malloc( sizeof(GLubyte) * 3 * w*h );
   rows        = malloc( sizeof(png_bytep) * h );

   /* Read pixels from buffer -- SLOW. */
   glPixelStorei(GL_PACK_ALIGNMENT, 1); /* Force them to pack the bytes. */
   glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, screenbuf );

   /* Convert data. */
   for (i = 0; i < h; i++)
      rows[i] = &screenbuf[ (h - i - 1) * (3*w) ];

   /* Save PNG. */
   write_png( filename, rows, w, h, PNG_COLOR_TYPE_RGB, 8);

   /* Check to see if an error occurred. */
   gl_checkErr();

   /* Free memory. */
   free( screenbuf );
   free( rows );
}
Example #2
0
/**
 * @brief Reloads new data or grows the size of the vbo.
 *
 *    @param vbo VBO to get new data of.
 *    @param size Size of new data.
 *    @param data New data.
 */
void gl_vboData( gl_vbo *vbo, GLsizei size, void* data )
{
    GLenum usage;

    vbo->size = size;

    if (has_vbo) {
        /* Get usage. */
        if (vbo->type == NGL_VBO_STREAM)
            usage = GL_STREAM_DRAW;
        else if (vbo->type == NGL_VBO_STATIC)
            usage = GL_STATIC_DRAW;
        else
            usage = GL_STREAM_DRAW;

        /* Get new data. */
        nglBindBuffer( GL_ARRAY_BUFFER, vbo->id );
        nglBufferData( GL_ARRAY_BUFFER, size, data, usage );
    }
    else {
        /* Grow memory. */
        vbo->data = realloc( vbo->data, size );
        if (data == NULL)
            memset( vbo->data, 0, size );
        else
            memcpy( vbo->data, data, size );
    }

    /* Check for errors. */
    gl_checkErr();
}
Example #3
0
/**
 * @brief Loads sur into tex, checks for expected size of w and h.
 *
 *    @param sur Surface to load into texture.
 *    @param w Expected width of surface.
 *    @param h Expected height of surface.
 *    @param tex Already generated texture to load into.
 *    @return 0 on success;
 */
static int nebu_loadTexture( SDL_Surface *sur, int w, int h, GLuint tex )
{
   SDL_Surface *nebu_sur;

   nebu_sur = gl_prepareSurface( sur );
   if ((w!=0) && (h!=0) &&
         ((nebu_sur->w != w) || (nebu_sur->h != h))) {
      WARN("Nebula size doesn't match expected! (%dx%d instead of %dx%d)",
            nebu_sur->w, nebu_sur->h, nebu_pw, nebu_ph );
      return -1;
   }

   /* Load the texture */
   glBindTexture( GL_TEXTURE_2D, tex );
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

   /* Store into opengl saving only alpha channel in video memory */
   SDL_LockSurface( nebu_sur );
   glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, nebu_sur->w, nebu_sur->h,
         0, GL_RGBA, GL_UNSIGNED_BYTE, nebu_sur->pixels );
   SDL_UnlockSurface( nebu_sur );

   SDL_FreeSurface(nebu_sur);
   gl_checkErr();
   return 0;
}
Example #4
0
File: naev.c Project: ekrumme/naev
/**
 * @brief Split main loop from main() for secondary loop hack in toolkit.c.
 */
void main_loop (void)
{
   int tk;

   /* Check to see if toolkit is open. */
   tk = toolkit_isOpen();

   /* Clear buffer. */
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   fps_control(); /* everyone loves fps control */

   sound_update( real_dt ); /* Update sounds. */
   if (tk) toolkit_update(); /* to simulate key repetition */
   if (!menu_isOpen(MENU_MAIN)) {
      if (!paused)
         update_all(); /* update game */
      render_all();
   }
   /* Toolkit is rendered on top. */
   if (tk) toolkit_render();

   gl_checkErr(); /* check error every loop */

   /* Draw buffer. */
   SDL_GL_SwapBuffers();
}
Example #5
0
/**
 * @brief Creates a VBO.
 *
 *    @param target Target to create to (usually GL_ARRAY_BUFFER).
 *    @param size Size of the buffer (in bytes).
 *    @param data The actual datat to use.
 *    @param usage Usage to use.
 *    @return ID of the vbo.
 */
static gl_vbo* gl_vboCreate( GLenum target, GLsizei size, void* data, GLenum usage )
{
    gl_vbo *vbo;

    /* Allocate. */
    vbo = malloc( sizeof(gl_vbo) );
    memset( vbo, 0, sizeof(gl_vbo) );

    /* General stuff. */
    vbo->size = size;

    if (has_vbo) {
        /* Create the buffer. */
        nglGenBuffers( 1, &vbo->id );

        /* Upload the data. */
        nglBindBuffer( target, vbo->id );
        nglBufferData( target, size, data, usage );
        nglBindBuffer( target, 0 );
    }
    else {
        vbo->size = size;
        vbo->data = malloc(size);
        if (data == NULL)
            memset( vbo->data, 0, size );
        else
            memcpy( vbo->data, data, size );
    }

    /* Check for errors. */
    gl_checkErr();

    return vbo;
}
Example #6
0
/**
 * @brief Renders a rectangle.
 *
 *    @param x X position to render rectangle at.
 *    @param y Y position to render rectangle at.
 *    @param w Rectangle width.
 *    @param h Rectangle height.
 *    @param c Rectangle colour.
 */
void gl_renderRectEmpty( double x, double y, double w, double h, const glColour *c )
{
   GLfloat vx, vy, vxw, vyh;
   GLfloat vertex[5*2], col[5*4];

   /* Helper variables. */
   vx  = (GLfloat) x;
   vy  = (GLfloat) y;
   vxw = vx + (GLfloat) w;
   vyh = vy + (GLfloat) h;

   /* Set the vertex. */
   vertex[0] = vx;
   vertex[1] = vy;
   vertex[2] = vxw;
   vertex[3] = vy;
   vertex[4] = vxw;
   vertex[5] = vyh;
   vertex[6] = vx;
   vertex[7] = vyh;
   vertex[8] = vx;
   vertex[9] = vy;
   gl_vboSubData( gl_renderVBO, 0, sizeof(vertex), vertex );
   gl_vboActivateOffset( gl_renderVBO, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );

   /* Set the colour. */
   col[0] = c->r;
   col[1] = c->g;
   col[2] = c->b;
   col[3] = c->a;
   col[4] = col[0];
   col[5] = col[1];
   col[6] = col[2];
   col[7] = col[3];
   col[8] = col[0];
   col[9] = col[1];
   col[10] = col[2];
   col[11] = col[3];
   col[12] = col[0];
   col[13] = col[1];
   col[14] = col[2];
   col[15] = col[3];
   col[16] = col[0];
   col[17] = col[1];
   col[18] = col[2];
   col[19] = col[3];
   gl_vboSubData( gl_renderVBO, gl_renderVBOcolOffset, sizeof(col), col );
   gl_vboActivateOffset( gl_renderVBO, GL_COLOR_ARRAY,
         gl_renderVBOcolOffset, 4, GL_FLOAT, 0 );

   /* Draw. */
   glDrawArrays( GL_LINE_STRIP, 0, 5 );

   /* Clear state. */
   gl_vboDeactivate();

   /* Check errors. */
   gl_checkErr();
}
Example #7
0
/**
 * @brief Unmaps a buffer.
 *
 *    @param vbo VBO to unmap.
 */
void gl_vboUnmap( gl_vbo *vbo )
{
    (void) vbo;
    if (has_vbo)
        nglUnmapBuffer( GL_ARRAY_BUFFER );

    /* Check for errors. */
    gl_checkErr();
}
Example #8
0
File: font.c Project: nenau/naev
/**
 * @brief Ends the rendering engine.
 */
static void gl_fontRenderEnd (void)
{
   gl_vboDeactivate();
   gl_matrixPop();
   gl_matrixMode( GL_PROJECTION );
   glDisable(GL_TEXTURE_2D);

   /* Check for errors. */
   gl_checkErr();
}
Example #9
0
/**
 * @brief Creates a stream vbo.
 *
 *    @param size Size of the stream vbo (multiply by sizeof(type)).
 *    @param data Data for the VBO.
 */
gl_vbo* gl_vboCreateStatic( GLsizei size, void* data )
{
    gl_vbo *vbo;

    vbo = gl_vboCreate( GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW );
    vbo->type = NGL_VBO_STATIC;

    /* Check for errors. */
    gl_checkErr();

    return vbo;
}
Example #10
0
/**
 * @brief Loads some data into the VBO.
 *
 *    @param vbo VBO to load data into.
 *    @param offset Offset location of the data (in bytes).
 *    @param size Size of the data (in bytes).
 *    @param data Pointer to the data.
 */
void gl_vboSubData( gl_vbo *vbo, GLint offset, GLsizei size, void* data )
{
    if (has_vbo) {
        nglBindBuffer( GL_ARRAY_BUFFER, vbo->id );
        nglBufferSubData( GL_ARRAY_BUFFER, offset, size, data );
    }
    else {
        memcpy( &vbo->data[offset], data, size );
    }

    /* Check for errors. */
    gl_checkErr();
}
Example #11
0
/**
 * @brief Renders a rectangle.
 *
 *    @param x X position to render rectangle at.
 *    @param y Y position to render rectangle at.
 *    @param w Rectangle width.
 *    @param h Rectangle height.
 *    @param c Rectangle colour.
 */
void gl_renderRect( double x, double y, double w, double h, const glColour *c )
{
   GLfloat vertex[4*2], col[4*4];

   /* Set the vertex. */
   /*   1--2
    *   |  |
    *   3--4
    */
   vertex[0] = (GLfloat)x;
   vertex[4] = vertex[0];
   vertex[2] = vertex[0] + (GLfloat)w;
   vertex[6] = vertex[2];
   vertex[1] = (GLfloat)y;
   vertex[3] = vertex[1];
   vertex[5] = vertex[1] + (GLfloat)h;
   vertex[7] = vertex[5];
   gl_vboSubData( gl_renderVBO, 0, 4*2*sizeof(GLfloat), vertex );
   gl_vboActivateOffset( gl_renderVBO, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );

   /* Set the colour. */
   col[0] = c->r;
   col[1] = c->g;
   col[2] = c->b;
   col[3] = c->a;
   col[4] = col[0];
   col[5] = col[1];
   col[6] = col[2];
   col[7] = col[3];
   col[8] = col[0];
   col[9] = col[1];
   col[10] = col[2];
   col[11] = col[3];
   col[12] = col[0];
   col[13] = col[1];
   col[14] = col[2];
   col[15] = col[3];
   gl_vboSubData( gl_renderVBO, gl_renderVBOcolOffset, 4*4*sizeof(GLfloat), col );
   gl_vboActivateOffset( gl_renderVBO, GL_COLOR_ARRAY,
         gl_renderVBOcolOffset, 4, GL_FLOAT, 0 );

   /* Draw. */
   glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );

   /* Clear state. */
   gl_vboDeactivate();

   /* Check errors. */
   gl_checkErr();
}
Example #12
0
/**
 * @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();
}
Example #13
0
/**
 * @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();
}
Example #14
0
File: opengl.c Project: Dinth/naev
/**
 * @brief Initializes SDL/OpenGL and the works.
 *    @return 0 on success.
 */
int gl_init (void)
{
   unsigned int flags;
   int dw, dh;

   /* Defaults. */
   dw = gl_screen.desktop_w;
   dh = gl_screen.desktop_h;
   memset( &gl_screen, 0, sizeof(gl_screen) );
   flags  = SDL_OPENGL;
   gl_screen.desktop_w = dw;
   gl_screen.desktop_h = dh;

   /* Load configuration. */
   if (conf.vsync)
      gl_screen.flags |= OPENGL_VSYNC;
   gl_screen.w = conf.width;
   gl_screen.h = conf.height;
   gl_setScale( conf.scalefactor );
   if (conf.fullscreen) {
      gl_screen.flags |= OPENGL_FULLSCREEN;
      flags |= SDL_FULLSCREEN;
   }

   /* Initializes Video */
   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
      WARN("Unable to initialize SDL Video: %s", SDL_GetError());
      return -1;
   }

   /* Set opengl flags. */
   gl_setupAttributes();

   /* See if should set up fullscreen. */
   if (conf.fullscreen)
      gl_setupFullscreen( &flags );

   /* Create the window. */
   gl_createWindow( flags );

   /* Some OpenGL options. */
   glClearColor( 0., 0., 0., 1. );

   /* Set default opengl state. */
   gl_defState();

   /* Set up possible scaling. */
   gl_setupScaling();

   /* Handle setting the default viewport. */
   gl_setDefViewport( 0, 0, gl_screen.rw, gl_screen.rh );
   gl_defViewport();

   /* Finishing touches. */
   glClear( GL_COLOR_BUFFER_BIT ); /* must clear the buffer first */
   gl_checkErr();

   /* Load extensions. */
   gl_initExtensions();

   /* Start hinting. */
   gl_hint();

   /* Initialize subsystems.*/
   gl_initMatrix();
   gl_initTextures();
   gl_initVBO();
   gl_initRender();

   /* Get info about the OpenGL window */
   gl_getGLInfo();

   /* Cosmetic new line. */
   DEBUG("");

   return 0;
}
Example #15
0
/**
 * @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();
}
Example #16
0
/**
 * @brief Texture blitting backend for interpolated texture.
 *
 * Value blitted is  ta*inter + tb*(1.-inter).
 *
 *    @param ta Texture A to blit.
 *    @param tb Texture B to blit.
 *    @param inter Amount of interpolation to do.
 *    @param x X position of the texture on the screen.
 *    @param y Y position of the texture on the screen.
 *    @param tx X position within the texture.
 *    @param ty Y position within the texture.
 *    @param tw Texture width.
 *    @param th Texture height.
 *    @param c Colour to use (modifies texture colour).
 */
static void gl_blitTextureInterpolate(  const glTexture* ta,
      const glTexture* tb, const double inter,
      const double x, const double y,
      const double w, const double h,
      const double tx, const double ty,
      const double tw, const double th, const glColour *c )
{
   GLfloat vertex[4*2], tex[4*2], col[4*4];
   GLfloat mcol[4] = { 0., 0., 0. };

   /* No interpolation. */
   if (!conf.interpolate || (tb == NULL)) {
      gl_blitTexture( ta, x, y, w, h, tx, ty, tw, th, c );
      return;
   }

   /* Corner cases. */
   if (inter == 1.) {
      gl_blitTexture( ta, x, y, w, h, tx, ty, tw, th, c );
      return;
   }
   else if (inter == 0.) {
      gl_blitTexture( tb, x, y, w, h, tx, ty, tw, th, c );
      return;
   }

   /* No multitexture. */
   if (nglActiveTexture == NULL) {
      if (inter > 0.5)
         gl_blitTexture( ta, x, y, w, h, tx, ty, tw, th, c );
      else
         gl_blitTexture( tb, x, y, w, h, tx, ty, tw, th, c );
   }

   /* Set default colour. */
   if (c == NULL)
      c = &cWhite;

   /* Bind the textures. */
   /* Texture 0. */
   nglActiveTexture( GL_TEXTURE0 );
   glEnable(GL_TEXTURE_2D);
   glBindTexture( GL_TEXTURE_2D, ta->texture);

   /* Set the mode. */
   glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );

   /* Interpolate texture and alpha. */
   glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB,      GL_INTERPOLATE );
   glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA,    GL_INTERPOLATE );
   mcol[3] = inter;
   glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, mcol );

   /* Arguments. */
   /* Arg0. */
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB,    GL_TEXTURE0 );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB,   GL_SRC_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA,  GL_TEXTURE0 );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
   /* Arg1. */
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB,    GL_TEXTURE1 );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB,   GL_SRC_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA,  GL_TEXTURE1 );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
   /* Arg2. */
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB,    GL_CONSTANT );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB,   GL_SRC_ALPHA );
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_ALPHA,  GL_CONSTANT );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA );

   /* Texture 1. */
   nglActiveTexture( GL_TEXTURE1 );
   glEnable(GL_TEXTURE_2D);
   glBindTexture( GL_TEXTURE_2D, tb->texture);

   /* Set the mode. */
   glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );

   /* Interpolate texture and alpha. */
   glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB,      GL_MODULATE );
   glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA,    GL_MODULATE );

   /* Arguments. */
   /* Arg0. */
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB,    GL_PREVIOUS );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB,   GL_SRC_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA,  GL_PREVIOUS );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
   /* Arg1. */
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB,    GL_PRIMARY_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB,   GL_SRC_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA,  GL_PRIMARY_COLOR );
   glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );

   /* Set the colour. */
   col[0] = c->r;
   col[1] = c->g;
   col[2] = c->b;
   col[3] = c->a;
   col[4] = col[0];
   col[5] = col[1];
   col[6] = col[2];
   col[7] = col[3];
   col[8] = col[0];
   col[9] = col[1];
   col[10] = col[2];
   col[11] = col[3];
   col[12] = col[0];
   col[13] = col[1];
   col[14] = col[2];
   col[15] = col[3];
   gl_vboSubData( gl_renderVBO, gl_renderVBOcolOffset, 4*4*sizeof(GLfloat), col );
   gl_vboActivateOffset( gl_renderVBO, GL_COLOR_ARRAY,
         gl_renderVBOcolOffset, 4, GL_FLOAT, 0 );

   /* Set the vertex. */
   vertex[0] = (GLfloat)x;
   vertex[4] = vertex[0];
   vertex[2] = vertex[0] + (GLfloat)w;
   vertex[6] = vertex[2];
   vertex[1] = (GLfloat)y;
   vertex[3] = vertex[1];
   vertex[5] = vertex[1] + (GLfloat)h;
   vertex[7] = vertex[5];
   gl_vboSubData( gl_renderVBO, 0, 4*2*sizeof(GLfloat), vertex );
   gl_vboActivateOffset( gl_renderVBO, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );

   /* Set the texture. */
   tex[0] = (GLfloat)tx;
   tex[4] = tex[0];
   tex[2] = tex[0] + (GLfloat)tw;
   tex[6] = tex[2];
   tex[1] = (GLfloat)ty;
   tex[3] = tex[1];
   tex[5] = tex[1] + (GLfloat)th;
   tex[7] = tex[5];
   gl_vboSubData( gl_renderVBO, gl_renderVBOtexOffset, 4*2*sizeof(GLfloat), tex );
   gl_vboActivateOffset( gl_renderVBO, GL_TEXTURE0,
         gl_renderVBOtexOffset, 2, GL_FLOAT, 0 );
   gl_vboActivateOffset( gl_renderVBO, GL_TEXTURE1,
         gl_renderVBOtexOffset, 2, GL_FLOAT, 0 );

   /* Draw. */
   glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );

   /* Clear state. */
   gl_vboDeactivate();
   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();
}
Example #17
0
/**
 * @brief Texture blitting backend.
 *
 *    @param texture Texture to blit.
 *    @param x X position of the texture on the screen. (units pixels)
 *    @param y Y position of the texture on the screen. (units pixels)
 *    @param w Width on the screen. (units pixels)
 *    @param h Height on the screen. (units pixels)
 *    @param tx X position within the texture. [0:1]
 *    @param ty Y position within the texture. [0:1]
 *    @param tw Texture width. [0:1]
 *    @param th Texture height. [0:1]
 *    @param c Colour to use (modifies texture colour).
 */
void gl_blitTexture(  const glTexture* texture,
      const double x, const double y,
      const double w, const double h,
      const double tx, const double ty,
      const double tw, const double th, const glColour *c )
{
   GLfloat vertex[4*2], tex[4*2], col[4*4];

   /* Bind the texture. */
   glEnable(GL_TEXTURE_2D);
   glBindTexture( GL_TEXTURE_2D, texture->texture);

   /* Must have colour for now. */
   if (c == NULL)
      c = &cWhite;

   /* Set the vertex. */
   vertex[0] = (GLfloat)x;
   vertex[4] = vertex[0];
   vertex[2] = vertex[0] + (GLfloat)w;
   vertex[6] = vertex[2];
   vertex[1] = (GLfloat)y;
   vertex[3] = vertex[1];
   vertex[5] = vertex[1] + (GLfloat)h;
   vertex[7] = vertex[5];
   gl_vboSubData( gl_renderVBO, 0, 4*2*sizeof(GLfloat), vertex );
   gl_vboActivateOffset( gl_renderVBO, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 );

   /* Set the texture. */
   tex[0] = (GLfloat)tx;
   tex[4] = tex[0];
   tex[2] = tex[0] + (GLfloat)tw;
   tex[6] = tex[2];
   tex[1] = (GLfloat)ty;
   tex[3] = tex[1];
   tex[5] = tex[1] + (GLfloat)th;
   tex[7] = tex[5];
   gl_vboSubData( gl_renderVBO, gl_renderVBOtexOffset, 4*2*sizeof(GLfloat), tex );
   gl_vboActivateOffset( gl_renderVBO, GL_TEXTURE_COORD_ARRAY,
         gl_renderVBOtexOffset, 2, GL_FLOAT, 0 );

   /* Set the colour. */
   col[0] = c->r;
   col[1] = c->g;
   col[2] = c->b;
   col[3] = c->a;
   col[4] = col[0];
   col[5] = col[1];
   col[6] = col[2];
   col[7] = col[3];
   col[8] = col[0];
   col[9] = col[1];
   col[10] = col[2];
   col[11] = col[3];
   col[12] = col[0];
   col[13] = col[1];
   col[14] = col[2];
   col[15] = col[3];
   gl_vboSubData( gl_renderVBO, gl_renderVBOcolOffset, 4*4*sizeof(GLfloat), col );
   gl_vboActivateOffset( gl_renderVBO, GL_COLOR_ARRAY,
         gl_renderVBOcolOffset, 4, GL_FLOAT, 0 );

   /* Draw. */
   glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );

   /* Clear state. */
   gl_vboDeactivate();
   glDisable(GL_TEXTURE_2D);

   /* anything failed? */
   gl_checkErr();
}
Example #18
0
/**
 * @brief Loads a surface into an opengl texture.
 *
 *    @param surface Surface to load into a texture.
 *    @param flags Flags to use.
 *    @param[out] rw Real width of the texture.
 *    @param[out] rh Real height of the texture.
 *    @return The opengl texture id.
 */
static GLuint gl_loadSurface( SDL_Surface* surface, int *rw, int *rh, unsigned int flags, int freesur )
{
   GLuint texture;
   GLfloat param;

   /* Prepare the surface. */
   surface = gl_prepareSurface( surface );
   if (rw != NULL)
      (*rw) = surface->w;
   if (rh != NULL)
      (*rh) = surface->h;

   /* opengl texture binding */
   glGenTextures( 1, &texture ); /* Creates the texture */
   glBindTexture( GL_TEXTURE_2D, texture ); /* Loads the texture */

   /* Filtering, LINEAR is better for scaling, nearest looks nicer, LINEAR
    * also seems to create a bit of artifacts around the edges */
   if ((gl_screen.scale != 1.) || (flags & OPENGL_TEX_MIPMAPS)) {
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   }
   else {
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   }

   /* Always wrap just in case. */
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

   /* now lead the texture data up */
   SDL_LockSurface( surface );
   if (gl_texHasCompress()) {
      glTexImage2D( GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA,
            surface->w, surface->h, 0, GL_RGBA,
            GL_UNSIGNED_BYTE, surface->pixels );
   }
   else {
      glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
            surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
   }
   SDL_UnlockSurface( surface );

   /* Create mipmaps. */
   if ((flags & OPENGL_TEX_MIPMAPS) && gl_texHasMipmaps()) {
      /* Do fancy stuff. */
      if (gl_hasExt("GL_EXT_texture_filter_anisotropic")) {
         glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &param);
         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, param);
      }
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 9);

      /* Now generate the mipmaps. */
      nglGenerateMipmap(GL_TEXTURE_2D);
   }

   /* cleanup */
   if (freesur)
      SDL_FreeSurface( surface );
   gl_checkErr();

   return texture;
}
Example #19
0
File: weapon.c Project: zid/naev
/**
 * @brief Renders an individual weapon.
 *
 *    @param w Weapon to render.
 *    @param dt Current delta tick.
 */
static void weapon_render( Weapon* w, const double dt )
{
   double x,y, cx,cy, gx,gy;
   glTexture *gfx;
   double z;
   glColour c = { .r=1., .g=1., .b=1. };

   switch (w->outfit->type) {
      /* Weapons that use sprites. */
      case OUTFIT_TYPE_AMMO:
      case OUTFIT_TYPE_TURRET_AMMO:
      case OUTFIT_TYPE_BOLT:
      case OUTFIT_TYPE_TURRET_BOLT:
         gfx = outfit_gfx(w->outfit);

         /* Alpha based on strength. */
         c.a = w->strength;

         /* Outfit spins around. */
         if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_SPIN)) {
            /* Check timer. */
            w->anim -= dt;
            if (w->anim < 0.) {
               w->anim = outfit_spin(w->outfit);

               /* Increment sprite. */
               w->sprite++;
               if (w->sprite >= gfx->sx*gfx->sy)
                  w->sprite = 0;
            }

            /* Render. */
            if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
               gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
                     w->timer / w->life,
                     w->solid->pos.x, w->solid->pos.y,
                     w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
            else
               gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y,
                     w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
         }
         /* Outfit faces direction. */
         else {
            if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
               gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
                     w->timer / w->life,
                     w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
            else
               gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
         }
         break;

      /* Beam weapons. */
      case OUTFIT_TYPE_BEAM:
      case OUTFIT_TYPE_TURRET_BEAM:
         gfx = outfit_gfx(w->outfit);

         /* Zoom. */
         gl_cameraZoomGet( &z );

         /* Position. */
         gl_cameraGet( &cx, &cy );
         gui_getOffset( &gx, &gy );
         x = (w->solid->pos.x - cx)*z + gx;
         y = (w->solid->pos.y - cy)*z + gy;

         /* Set up the matrix. */
         glMatrixMode(GL_PROJECTION);
         glPushMatrix();
            glTranslated( x, y, 0. );
            glRotated( 270. + w->solid->dir / M_PI * 180., 0., 0., 1. );

         /* Preparatives. */
         glEnable(GL_TEXTURE_2D);
         glBindTexture( GL_TEXTURE_2D, gfx->texture);
         glShadeModel(GL_SMOOTH);

         /* Actual rendering. */
         glBegin(GL_QUAD_STRIP);

            /* Start faded. */
            ACOLOUR(cWhite, 0.);

            glTexCoord2d( w->anim, 0. );
            glVertex2d( -gfx->sh/2.*z, 0. );

            glTexCoord2d( w->anim, 1. );
            glVertex2d( +gfx->sh/2.*z, 0. );

            /* Full strength. */
            COLOUR(cWhite);

            glTexCoord2d( w->anim + 10. / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, 10.*z );

            glTexCoord2d( w->anim + 10. / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, 10.*z );

            glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z );

            glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z );

            /* Fades out. */
            ACOLOUR(cWhite, 0.);

            glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 0. );
            glVertex2d( -gfx->sh/2.*z, w->outfit->u.bem.range*z );

            glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 1. );
            glVertex2d( +gfx->sh/2.*z, w->outfit->u.bem.range*z );
         glEnd(); /* GL_QUAD_STRIP */

         /* Do the beam movement. */
         w->anim -= 5. * dt;
         if (w->anim <= -gfx->sw)
            w->anim += gfx->sw;

         /* Clean up. */
         glDisable(GL_TEXTURE_2D);
         glShadeModel(GL_FLAT);
         glPopMatrix(); /* GL_PROJECTION */
         gl_checkErr();
         break;

      default:
         WARN("Weapon of type '%s' has no render implemented yet!",
               w->outfit->name);
         break;
   }
}


/**
 * @brief Checks to see if the weapon can hit the pilot.
 *
 *    @param w Weapon to check if hits pilot.
 *    @param p Pilot to check if is hit by weapon.
 *    @return 1 if can be hit, 0 if can't.
 */
static int weapon_checkCanHit( Weapon* w, Pilot *p )
{
   Pilot *parent;

   /* Can't hit invincible stuff. */
   if (pilot_isFlag(p, PILOT_INVINCIBLE))
      return 0;

   /* Can never hit same faction. */
   if (p->faction == w->faction)
      return 0;

   /* Go "through" dead pilots. */
   if (pilot_isFlag(p, PILOT_DEAD))
      return 0;

   /* Player behaves differently. */
   if (w->faction == FACTION_PLAYER) {

      /* Always hit without safety. */
      if (!weapon_safety)
         return 1;

      /* Always hit target. */
      else if (w->target == p->id)
         return 1;

      /* Always hit hostiles. */
      else if (pilot_isFlag(p, PILOT_HOSTILE))
         return 1;

      /* Always hit unbribed enemies. */
      else if (!pilot_isFlag(p, PILOT_BRIBED) &&
            areEnemies(w->faction, p->faction))
        return 1;

      /* Miss rest - can be neutral/ally. */
      else
         return 0;
   }

   /* Let hostiles hit player. */
   if (p->faction == FACTION_PLAYER) {
      parent = pilot_get(w->parent);
      if (parent != NULL) {
         if (pilot_isFlag(parent, PILOT_BRIBED))
            return 0;
         if (pilot_isFlag(parent, PILOT_HOSTILE))
            return 1;
      }
   }

   /* Hit non-allies. */
   if (areEnemies(w->faction, p->faction))
      return 1;

   return 0;
}
Example #20
0
File: font.c Project: nenau/naev
/**
 * @brief Adds a font glyph to the texture stash.
 */
static int gl_fontAddGlyphTex( glFontStash *stsh, font_char_t *ch, glFontGlyph *glyph )
{
   int i, j, n;
   GLubyte *data;
   glFontRow *r, *gr;
   glFontTex *tex;
   GLfloat *vbo_tex;
   GLshort *vbo_vert;
   GLfloat tx, ty, txw, tyh;
   GLfloat fw, fh;
   GLshort vx, vy, vw, vh;

   /* Find free row. */
   gr = NULL;
   for (i=0; i<array_size( stsh->tex ); i++) {
      for (j=0; j<MAX_ROWS; j++) {
         r = &stsh->tex->rows[j];
         /* Fits in current row, so use that. */
         if ((r->h == ch->h) && (r->x+ch->w < stsh->tw)) {
            tex = &stsh->tex[i];
            gr = r;
            break;
         }
         /* If not empty row, skip. */
         if (r->h != 0)
            continue;
         /* See if height fits. */
         if ((j==0) || (stsh->tex->rows[j-1].y+stsh->tex->rows[j-1].h+ch->h < stsh->th)) {
            r->h = ch->h;
            if (j>0)
               r->y = stsh->tex->rows[j-1].y + stsh->tex->rows[j-1].h;
            tex = &stsh->tex[i];
            gr = r;
            break;
         }
      }
   }

   /* Allocate new texture. */
   if (gr == NULL) {
      tex = &array_grow( &stsh->tex );
      memset( stsh->tex, 0, sizeof(glFontTex) );

      glGenTextures( 1, &tex->id );
      glBindTexture( GL_TEXTURE_2D, tex->id );

      /* Shouldn't ever scale - we'll generate appropriate size font. */
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

      /* Clamp texture .*/
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

      /* Initialize size. */
      data = calloc( 2*stsh->tw*stsh->th, sizeof(GLubyte) );
      glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, stsh->tw, stsh->th, 0,
            GL_ALPHA, GL_UNSIGNED_BYTE, data );
      free(data);

      /* Check for errors. */
      gl_checkErr();

      gr = &tex->rows[0];
      gr->h = ch->h;
   }

   /* Upload data. */
   glBindTexture( GL_TEXTURE_2D, tex->id );
   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
   glTexSubImage2D( GL_TEXTURE_2D, 0, gr->x, gr->y, ch->w, ch->h,
         GL_ALPHA, GL_UNSIGNED_BYTE, ch->data );

   /* Check for error. */
   gl_checkErr();

   /* Update VBOs. */
   stsh->nvbo++;
   if (stsh->nvbo > stsh->mvbo) {
      stsh->mvbo *= 2;
      stsh->vbo_tex_data  = realloc( stsh->vbo_tex_data,  8*stsh->mvbo*sizeof(GLfloat) );
      stsh->vbo_vert_data = realloc( stsh->vbo_vert_data, 8*stsh->mvbo*sizeof(GLshort) );
   }
   n = 8*stsh->nvbo;
   vbo_tex  = &stsh->vbo_tex_data[n-8];
   vbo_vert = &stsh->vbo_vert_data[n-8];
   /* We do something like the following for vertex coordinates.
      *
      *
      *  +----------------- top reference   \  <------- font->h
      *  |                                  |
      *  |                                  | --- off_y
      *  +----------------- glyph top       /
      *  |
      *  |
      *  +----------------- glyph bottom
      *  |
      *  v   y
      *
      *
      *  +----+------------->  x
      *  |    |
      *  |    glyph start
      *  |
      *  side reference
      *
      *  \----/
      *   off_x
      */
   /* Temporary variables. */
   fw  = (GLfloat) stsh->tw;
   fh  = (GLfloat) stsh->th;
   tx  = (GLfloat) gr->x / fw;
   ty  = (GLfloat) gr->y / fh;
   txw = (GLfloat) (gr->x + ch->w) / fw;
   tyh = (GLfloat) (gr->y + ch->h) / fh;
   vx  = ch->off_x;
   vy  = ch->off_y - ch->h;
   vw  = ch->w;
   vh  = ch->h;
   /* Texture coords. */
   vbo_tex[ 0 ] = tx;  /* Top left. */
   vbo_tex[ 1 ] = ty;
   vbo_tex[ 2 ] = txw; /* Top right. */
   vbo_tex[ 3 ] = ty;
   vbo_tex[ 4 ] = txw; /* Bottom right. */
   vbo_tex[ 5 ] = tyh;
   vbo_tex[ 6 ] = tx;  /* Bottom left. */
   vbo_tex[ 7 ] = tyh;
   /* Vertex coords. */
   vbo_vert[ 0 ] = vx;    /* Top left. */
   vbo_vert[ 1 ] = vy+vh;
   vbo_vert[ 2 ] = vx+vw; /* Top right. */
   vbo_vert[ 3 ] = vy+vh;
   vbo_vert[ 4 ] = vx+vw; /* Bottom right. */
   vbo_vert[ 5 ] = vy;
   vbo_vert[ 6 ] = vx;    /* Bottom left. */
   vbo_vert[ 7 ] = vy;
   /* Update vbos. */
   gl_vboData( stsh->vbo_tex,  sizeof(GLfloat)*n,  stsh->vbo_tex_data );
   gl_vboData( stsh->vbo_vert, sizeof(GLshort)*n, stsh->vbo_vert_data );

   /* Add space for the new character. */
   gr->x += ch->w;

   /* Save glyph data. */
   glyph->vbo_id = (n-8)/2;
   glyph->tex = tex;

   /* Since the VBOs have possibly changed, we have to reset the data. */
   gl_vboActivateOffset( stsh->vbo_tex,  GL_TEXTURE_COORD_ARRAY, 0, 2, GL_FLOAT, 0 );
   gl_vboActivateOffset( stsh->vbo_vert, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );

   return 0;
}