Cinematics

I’m going to feel stupid for asking this, but take for instance Quake 2 in OpenGL mode and Quake 3 based games, which use OpenGL exclusively. How are the full screen cinematics rendered? I can’t imagine frames are decoded into textures and uploaded, displayed and then discarded so rapidly.

Just look at the source code for Quake2. It is GPL’ed. http://www.idsoftware.com/business/techdownloads/

Well, I was interested in it too, so… I found it.

There are 2 interesting files, cl_cin.c and gl_draw.c as well as some *.h to change names.

Sending data to the card is indeed quite fast. And you only need between 20 to 60 fps.

As you can see, after loading actual 256 colors paletted pixel data, for each frame, it uploads it to an Opengl texture, with a special case if ColorTableExt is not available.

/*

Draw_StretchRaw

*/
extern unsigned r_rawpalette[256];

void Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte data)
{
unsigned image32[256
256];
unsigned char image8[256*256];
int i, j, trows;
byte *source;
int frac, fracstep;
float hscale;
int row;
float t;

GL_Bind (0);

if (rows<=256)
{
hscale = 1;
trows = rows;
}
else
{
hscale = rows/256.0;
trows = 256;
}
t = rows*hscale / 256;

if ( !qglColorTableEXT )
{
unsigned *dest;

  for (i=0 ; i<trows ; i++)
  {
  	row = (int)(i*hscale);
  	if (row > rows)
  		break;
  	source = data + cols*row;
  	dest = &image32[i*256];
  	fracstep = cols*0x10000/256;
  	frac = fracstep >> 1;
  	for (j=0 ; j<256 ; j++)
  	{
  		dest[j] = r_rawpalette[source[frac>>16]];
  		frac += fracstep;
  	}
  }

  qglTexImage2D (GL_TEXTURE_2D, 0, gl_tex_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, image32);

}
else
{
unsigned char *dest;

  for (i=0 ; i<trows ; i++)
  {
  	row = (int)(i*hscale);
  	if (row > rows)
  		break;
  	source = data + cols*row;
  	dest = &image8[i*256];
  	fracstep = cols*0x10000/256;
  	frac = fracstep >> 1;
  	for (j=0 ; j<256 ; j++)
  	{
  		dest[j] = source[frac>>16];
  		frac += fracstep;
  	}
  }

  qglTexImage2D( GL_TEXTURE_2D, 
  	           0, 
  			   GL_COLOR_INDEX8_EXT, 
  			   256, 256, 
  			   0, 
  			   GL_COLOR_INDEX, 
  			   GL_UNSIGNED_BYTE, 
  			   image8 );

}
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

if ( ( gl_config.renderer == GL_RENDERER_MCD ) | | ( gl_config.renderer & GL_RENDERER_RENDITION ) )
qglDisable (GL_ALPHA_TEST);

qglBegin (GL_QUADS);
qglTexCoord2f (0, 0);
qglVertex2f (x, y);
qglTexCoord2f (1, 0);
qglVertex2f (x+w, y);
qglTexCoord2f (1, t);
qglVertex2f (x+w, y+h);
qglTexCoord2f (0, t);
qglVertex2f (x, y+h);
qglEnd ();

if ( ( gl_config.renderer == GL_RENDERER_MCD ) | | ( gl_config.renderer & GL_RENDERER_RENDITION ) )
qglEnable (GL_ALPHA_TEST);
}