/** * @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 ); }
/** * @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(); }
/** * @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; }
/** * @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(); }
/** * @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; }
/** * @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(); }
/** * @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(); }
/** * @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(); }
/** * @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; }
/** * @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(); }
/** * @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(); }
/** * @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 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; }
/** * @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 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(); }
/** * @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(); }
/** * @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, ¶m); 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; }
/** * @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; }
/** * @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; }