This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/render/r_cin.c
2022-06-27 01:14:44 +03:00

424 lines
9.3 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_cin.c
#include "r_local.h"
#include "cin.h"
#define MAX_CINEMATICS 256
typedef struct r_cinhandle_s
{
unsigned int id;
char *name;
cinematics_t *cin;
texture_t *image;
struct r_cinhandle_s *prev, *next;
} r_cinhandle_t;
static byte *r_cinMemPool;
static r_cinhandle_t *r_cinematics;
static r_cinhandle_t r_cinematics_headnode, *r_free_cinematics;
#define Cin_Malloc(size) Mem_Alloc(r_cinMemPool,size)
#define Cin_Free(data) Mem_Free(data)
/*
==================
R_ReadNextRoQFrame
==================
*/
static byte *R_ReadNextRoQFrame( cinematics_t *cin )
{
roq_chunk_t *chunk = &cin->chunk;
while( !FS_Eof( cin->file ) )
{
RoQ_ReadChunk( cin );
if( FS_Eof( cin->file ) )
return NULL;
if( chunk->size <= 0 )
continue;
if( chunk->id == RoQ_INFO )
RoQ_ReadInfo( cin );
else if( chunk->id == RoQ_QUAD_VQ )
return RoQ_ReadVideo( cin );
else if( chunk->id == RoQ_QUAD_CODEBOOK )
RoQ_ReadCodebook( cin );
else
RoQ_SkipChunk( cin );
}
return NULL;
}
/*
==================
R_RunRoQ
==================
*/
static void R_RunRoQ( cinematics_t *cin )
{
uint frame;
frame = (RI.refdef.time - cin->time) * (float)(RoQ_FRAMERATE);
if( frame <= cin->frame )
return;
if( frame > cin->frame + 1 )
cin->time = RI.refdef.time - cin->frame / RoQ_FRAMERATE;
cin->pic = cin->pic_pending;
cin->pic_pending = R_ReadNextRoQFrame( cin );
if( !cin->pic_pending )
{
FS_Seek( cin->file, cin->headerlen, SEEK_SET );
cin->frame = 0;
cin->pic_pending = R_ReadNextRoQFrame( cin );
cin->time = RI.refdef.time;
}
cin->new_frame = true;
}
/*
==================
R_StopRoQ
==================
*/
static void R_StopRoQ( cinematics_t *cin )
{
cin->frame = 0;
cin->time = 0; // done
cin->pic = NULL;
cin->pic_pending = NULL;
if( cin->file )
{
FS_Close( cin->file );
cin->file = 0;
}
if( cin->name )
{
Mem_Free( cin->name );
cin->name = NULL;
}
if( cin->vid_buffer )
{
Mem_Free( cin->vid_buffer );
cin->vid_buffer = NULL;
}
}
/*
==================
R_OpenCinematics
==================
*/
static cinematics_t *R_OpenCinematics( char *filename )
{
file_t *file;
cinematics_t *cin = NULL;
roq_chunk_t *chunk = &cin->chunk;
if((file = FS_Open( filename, "rb" )) == NULL )
return NULL;
cin = Cin_Malloc( sizeof( cinematics_t ) );
cin->name = filename;
cin->file = file;
cin->mempool = r_cinMemPool;
// read header
RoQ_ReadChunk( cin );
chunk = &cin->chunk;
if( chunk->id != RoQ_HEADER1 || chunk->size != RoQ_HEADER2 || chunk->argument != RoQ_HEADER3 )
{
R_StopRoQ( cin );
Cin_Free( cin );
return NULL;
}
cin->headerlen = FS_Tell( cin->file );
cin->frame = 0;
cin->pic = cin->pic_pending = R_ReadNextRoQFrame( cin );
cin->time = RI.refdef.time;
cin->new_frame = true;
return cin;
}
/*
==================
R_ResampleCinematicFrame
==================
*/
static texture_t *R_ResampleCinematicFrame( r_cinhandle_t *handle )
{
texture_t *image;
cinematics_t *cin = handle->cin;
static rgbdata_t r_cin;
if( !cin->pic )
return NULL;
if( !handle->image )
{
r_cin.width = cin->width;
r_cin.height = cin->height;
r_cin.type = PF_RGB_24;
r_cin.size = cin->width * cin->height * 3;
r_cin.flags = 0;
r_cin.palette = NULL;
r_cin.buffer = cin->pic;
r_cin.numMips = r_cin.depth = 1;
handle->image = R_LoadTexture( handle->name, &r_cin, 3, TF_CINEMATIC );
cin->new_frame = false;
}
if( !cin->new_frame )
return handle->image;
cin->new_frame = false;
image = handle->image;
GL_Bind( 0, image );
if( image->srcWidth != cin->width || image->srcHeight != cin->height )
pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image->srcWidth, image->srcHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, cin->pic );
else pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, image->srcWidth, image->srcHeight, GL_RGBA, GL_UNSIGNED_BYTE, cin->pic );
image->srcWidth = cin->width;
image->srcHeight = cin->height;
return image;
}
//==================================================================================
/*
==================
R_CinList_f
==================
*/
void R_CinList_f( void )
{
cinematics_t *cin;
texture_t *image;
r_cinhandle_t *handle, *hnode;
Msg( "Active cintematics:" );
hnode = &r_cinematics_headnode;
handle = hnode->prev;
if( handle == hnode )
{
Msg( " none\n" );
return;
}
Msg( "\n" );
do {
cin = handle->cin;
image = handle->image;
Com_Assert( cin == NULL );
if( image && (cin->width != image->width || cin->height != image->height) )
Msg( "%s %i(%i)x%i(%i) f:%i\n", cin->name, cin->width, image->width, cin->height, image->height, cin->frame );
else Msg( "%s %ix%i f:%i\n", cin->name, cin->width, cin->height, cin->frame );
handle = handle->next;
} while( handle != hnode );
}
/*
==================
R_InitCinematics
==================
*/
void R_InitCinematics( void )
{
int i;
r_cinMemPool = Mem_AllocPool( "Cinematics" );
r_cinematics = Cin_Malloc( sizeof( r_cinhandle_t ) * MAX_CINEMATICS );
memset( r_cinematics, 0, sizeof( r_cinhandle_t ) * MAX_CINEMATICS );
// link cinemtics
r_free_cinematics = r_cinematics;
r_cinematics_headnode.id = 0;
r_cinematics_headnode.prev = &r_cinematics_headnode;
r_cinematics_headnode.next = &r_cinematics_headnode;
for( i = 0; i < MAX_CINEMATICS - 1; i++ )
{
if( i < MAX_CINEMATICS - 1 )
r_cinematics[i].next = &r_cinematics[i+1];
r_cinematics[i].id = i + 1;
}
Cmd_AddCommand( "cinlist", R_CinList_f, "display loaded videos list" );
}
/*
==================
R_RunAllCinematics
==================
*/
void R_RunAllCinematics( void )
{
r_cinhandle_t *handle, *hnode, *next;
hnode = &r_cinematics_headnode;
for( handle = hnode->prev; handle != hnode; handle = next )
{
next = handle->prev;
R_RunRoQ( handle->cin );
}
}
/*
==================
R_UploadCinematics
==================
*/
texture_t *R_UploadCinematics( unsigned int id )
{
Com_Assert( (id > 0 && id <= MAX_CINEMATICS) == 0 );
return R_ResampleCinematicFrame( r_cinematics + id - 1 );
}
/*
==================
R_StartCinematic
==================
*/
unsigned int R_StartCinematics( const char *arg )
{
char *name = NULL, uploadName[128];
size_t name_size;
cinematics_t *cin = NULL;
r_cinhandle_t *handle, *hnode, *next;
name_size = strlen( "video/" ) + strlen( arg ) + strlen( ".roq" ) + 1;
name = Cin_Malloc( name_size );
com.snprintf( name, name_size, "video/%s", arg );
FS_DefaultExtension( name, ".roq" );
// find cinematics with the same name
hnode = &r_cinematics_headnode;
for( handle = hnode->prev; handle != hnode; handle = next )
{
next = handle->prev;
Com_Assert( handle->cin == NULL );
// reuse
if( !com.stricmp( handle->cin->name, name ) )
{
Cin_Free( name );
return handle->id;
}
}
// open the file, read header, etc
cin = R_OpenCinematics( name );
// take a free cinematic handle if possible
if( !r_free_cinematics || !cin )
{
Cin_Free( name );
return 0;
}
handle = r_free_cinematics;
r_free_cinematics = handle->next;
com.snprintf( uploadName, sizeof( uploadName ), "***r_cinematic%i***", handle->id-1 );
name_size = com.strlen( uploadName ) + 1;
handle->name = Cin_Malloc( name_size );
memcpy( handle->name, uploadName, name_size );
handle->cin = cin;
// put handle at the start of the list
handle->prev = &r_cinematics_headnode;
handle->next = r_cinematics_headnode.next;
handle->next->prev = handle;
handle->prev->next = handle;
return handle->id;
}
/*
=================
R_FreeCinematics
=================
*/
void R_FreeCinematics( unsigned int id )
{
r_cinhandle_t *handle;
handle = r_cinematics + id - 1;
if( !handle->cin )
return;
R_StopRoQ( handle->cin );
Cin_Free( handle->cin );
handle->cin = NULL;
Com_Assert( handle->name == NULL );
Cin_Free( handle->name );
handle->name = NULL;
// remove from linked active list
handle->prev->next = handle->next;
handle->next->prev = handle->prev;
// insert into linked free list
handle->next = r_free_cinematics;
r_free_cinematics = handle;
}
/*
==================
R_ShutdownCinematics
==================
*/
void R_ShutdownCinematics( void )
{
r_cinhandle_t *handle, *hnode, *next;
if( !r_cinMemPool )
return;
hnode = &r_cinematics_headnode;
for( handle = hnode->prev; handle != hnode; handle = next )
{
next = handle->prev;
R_FreeCinematics( handle->id );
}
Cin_Free( r_cinematics );
Mem_FreePool( &r_cinMemPool );
Cmd_RemoveCommand( "cinlist" );
}