Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic adaptaion of movie playback to the new API #220

Merged
merged 3 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions client/enginecallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,11 @@ inline void PlaySound( int iSound, float vol ) { gEngfuncs.pfnPlaySoundByIndex(
#define FREE_CINEMATIC (*gRenderfuncs.AVI_FreeVideo)
#define CIN_IS_ACTIVE (*gRenderfuncs.AVI_IsActive)
#define CIN_GET_VIDEO_INFO (*gRenderfuncs.AVI_GetVideoInfo)
#define CIN_GET_FRAME_NUMBER (*gRenderfuncs.AVI_GetVideoFrameNumber)
#define CIN_GET_FRAMEDATA (*gRenderfuncs.AVI_GetVideoFrame)
#define CIN_UPDATE_SOUND (*gRenderfuncs.AVI_StreamSound)
// #define CIN_GET_FRAME_NUMBER (*gRenderfuncs.AVI_GetVideoFrameNumber)
// #define CIN_GET_FRAMEDATA (*gRenderfuncs.AVI_GetVideoFrame)
// #define CIN_UPDATE_SOUND (*gRenderfuncs.AVI_StreamSound)
#define CIN_THINK (*gRenderfuncs.AVI_Think)
#define CIN_SET_PARM (*gRenderfuncs.AVI_SetParm)

// glcommands
#define GL_SelectTexture (*gRenderfuncs.GL_SelectTexture)
Expand Down
6 changes: 5 additions & 1 deletion client/render/gl_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,13 @@ typedef struct gl_fbo_s
typedef struct gl_movie_s
{
char name[32];
void *state;
struct movie_state_s *state;
float length; // total cinematic length
int xres, yres; // size of cinematic

bool finished;
bool sound_set;
bool texture_set;
} gl_movie_t;

typedef struct gl_texbuffer_s
Expand Down
73 changes: 37 additions & 36 deletions client/render/gl_movie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,9 @@ int R_PrecacheCinematic( const char *cinname )
if( !cinname || !*cinname )
return -1;

if( *cinname == '*' )
{
cinname++;
}

// not AVI file
if( Q_stricmp( UTIL_FileExtension( cinname ), "avi" ))
const char *ext = UTIL_FileExtension( cinname );
if( Q_stricmp( ext, "avi" ) && Q_stricmp( ext, "webm" )) // with ffmpeg we don't really have a limit here
return -1;

int i;
Expand Down Expand Up @@ -68,7 +64,13 @@ int R_PrecacheCinematic( const char *cinname )
}

ALERT( at_console, "Loading cinematic %s [%s]\n", cinname, "sound" );
tr.cinematics[i].state = OPEN_CINEMATIC( tr.cinematics[i].name, true );

// FIXME: engine is hardcoded to load file in media/ folder, must be fixed on engine side
const char *p = tr.cinematics[i].name;
if( !Q_strnicmp( p, "media/", 6 ))
p += 6;

tr.cinematics[i].state = (movie_state_s *)OPEN_CINEMATIC( p, true );

// grab info about movie
if( tr.cinematics[i].state != NULL )
Expand All @@ -79,6 +81,8 @@ int R_PrecacheCinematic( const char *cinname )

void R_InitCinematics( void )
{
// a1ba: this function is useless lmao
// it's called before WORLD_HAS_MOVIES bit set
const char *name, *ext;

// make sure what we have texture to draw cinematics
Expand Down Expand Up @@ -162,6 +166,7 @@ void R_UpdateCinematic( const msurface_t *surf )
if( cinhandle >= 0 && es->cintexturenum <= 0 )
es->cintexturenum = R_AllocateCinematicTexture( TF_NOMIPMAP );

// a1ba: isn't this kinda stupid? If movie isn't active anymore, we will never draw movie on it again
if( cinhandle == -1 || es->cintexturenum <= 0 || CIN_IS_ACTIVE( tr.cinematics[cinhandle].state ) == false )
{
// cinematic textures limit exceeded, so remove SURF_MOVIE flag
Expand All @@ -170,28 +175,18 @@ void R_UpdateCinematic( const msurface_t *surf )
}

gl_movie_t *cin = &tr.cinematics[cinhandle];
float cin_time;

if( FBitSet( RI->currententity->curstate.iuser1, CF_LOOPED_MOVIE ))
{
// advances cinematic time
cin_time = fmod( RI->currententity->curstate.fuser2, cin->length );
}
else
{
cin_time = RI->currententity->curstate.fuser2;
}

// read the next frame
int cin_frame = CIN_GET_FRAME_NUMBER( cin->state, cin_time );
if( cin->finished )
return;

// upload the new frame
if( cin_frame != es->checkcount )
if( !cin->texture_set )
{
GL_SelectTexture( GL_TEXTURE0 ); // doesn't matter. select 0-th unit just as default
byte *raw = CIN_GET_FRAMEDATA( cin->state, cin_frame );
CIN_UPLOAD_FRAME( tr.cinTextures[es->cintexturenum-1], cin->xres, cin->yres, cin->xres, cin->yres, raw );
es->checkcount = cin_frame;
CIN_SET_PARM( cin->state,
AVI_RENDER_TEXNUM, tr.cinTextures[es->cintexturenum-1],
AVI_RENDER_W, cin->xres,
AVI_RENDER_H, cin->yres,
AVI_PARM_LAST );
cin->texture_set = true;
}
}

Expand All @@ -208,18 +203,24 @@ void R_UpdateCinSound( cl_entity_t *e )
return;

gl_movie_t *cin = &tr.cinematics[cinhandle];
float cin_time;

if( FBitSet( e->curstate.iuser1, CF_LOOPED_MOVIE ))
if( cin->finished )
return;

if( !cin->sound_set )
{
// advances cinematic time
cin_time = fmod( e->curstate.fuser2, cin->length );
CIN_SET_PARM( cin->state,
AVI_ENTNUM, e->index,
AVI_VOLUME, static_cast<int>( VOL_NORM * 255 ),
AVI_ATTN, ATTN_NORM,
AVI_PARM_LAST );
cin->sound_set = true;
}
else

if( !CIN_THINK( cin->state )) // TODO: make a video manager that will call this each frame
{
cin_time = e->curstate.fuser2;
if( FBitSet( RI->currententity->curstate.iuser1, CF_LOOPED_MOVIE ))
CIN_SET_PARM( cin->state, AVI_REWIND, AVI_PARM_LAST );
else cin->finished = true;
}

// stream avi sound
CIN_UPDATE_SOUND( cin->state, e->index, VOL_NORM, ATTN_IDLE, cin_time );
}
}
29 changes: 19 additions & 10 deletions client/render/gl_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,19 +496,28 @@ static bool R_HandleLightEntity(cl_entity_t *ent)
return true;
}

// TODO: move this to a common function in gl_movie.cpp
gl_movie_t *cin = &tr.cinematics[hCin];

// advances cinematic time
float cin_time = fmod(entity.GetCinTime(), cin->length);

// read the next frame
int cin_frame = CIN_GET_FRAME_NUMBER(cin->state, cin_time);
if (cin_frame != dlight->lastframe)
if( !cin->finished )
{
// upload the new frame
byte *raw = CIN_GET_FRAMEDATA(cin->state, cin_frame);
CIN_UPLOAD_FRAME(tr.cinTextures[dlight->cinTexturenum - 1], cin->xres, cin->yres, cin->xres, cin->yres, raw);
dlight->lastframe = cin_frame;
if( !cin->texture_set )
{
CIN_SET_PARM( cin->state, AVI_RENDER_TEXNUM, tr.cinTextures[dlight->cinTexturenum - 1],
AVI_RENDER_W, cin->xres,
AVI_RENDER_H, cin->yres,
AVI_PARM_LAST );
cin->texture_set = true;
}

// running think here because we're usually thinking with audio, but dlight doesn't have audio

if( !CIN_THINK( cin->state )); // probably should be moved to some kind of global manager that will tick each frame
{
if( FBitSet( RI->currententity->curstate.iuser1, CF_LOOPED_MOVIE ))
CIN_SET_PARM( cin->state, AVI_REWIND, AVI_PARM_LAST );
else cin->finished = true;
}
}

if (entity.DisableShadows())
Expand Down
33 changes: 24 additions & 9 deletions common/render_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,21 @@ typedef struct decallist_s
modelstate_t studio_state; // studio decals only
} decallist_t;

enum movie_parms_e
{
AVI_PARM_LAST = 0, // marker for SetParm to end parse parsing arguments
AVI_RENDER_TEXNUM, // (int) sets texture to draw into, if 0 will draw to screen
AVI_RENDER_X, // (int) when set to screen, sets position where to draw
AVI_RENDER_Y,
AVI_RENDER_W, // (int) sets texture or screen width
AVI_RENDER_H, // set to -1 to draw full screen
AVI_REWIND, // no argument, rewind playback to the beginning
AVI_ENTNUM, // (int) entity number, -1 for no spatialization
AVI_VOLUME, // (int) volume from 0 to 255
AVI_ATTN,
};

struct movie_state_s;
struct ref_viewpass_s;

typedef struct render_api_s
Expand Down Expand Up @@ -198,16 +213,16 @@ typedef struct render_api_s
void (*R_EntityRemoveDecals)( struct model_s *mod ); // remove all the decals from specified entity (BSP only)

// AVIkit support
void *(*AVI_LoadVideo)( const char *filename, qboolean load_audio );
int (*AVI_GetVideoInfo)( void *Avi, int *xres, int *yres, float *duration ); // a1ba: changed longs to int
int (*AVI_GetVideoFrameNumber)( void *Avi, float time );
byte *(*AVI_GetVideoFrame)( void *Avi, int frame );
struct movie_state_s *(*AVI_LoadVideo)( const char *filename, qboolean load_audio );
int (*AVI_GetVideoInfo)( struct movie_state_s *Avi, int *xres, int *yres, float *duration ); // a1ba: changed longs to int
int (*AVI_GetVideoFrameNumber)( struct movie_state_s *Avi, float time );
byte *(*AVI_GetVideoFrame)( struct movie_state_s *Avi, int frame );
void (*AVI_UploadRawFrame)( int texture, int cols, int rows, int width, int height, const byte *data );
void (*AVI_FreeVideo)( void *Avi );
int (*AVI_IsActive)( void *Avi );
void (*AVI_StreamSound)( void *Avi, int entnum, float fvol, float attn, float synctime );
void (*AVI_Reserved0)( void ); // for potential interface expansion without broken compatibility
void (*AVI_Reserved1)( void );
void (*AVI_FreeVideo)( struct movie_state_s *Avi );
int (*AVI_IsActive)( struct movie_state_s *Avi );
void (*AVI_StreamSound)( struct movie_state_s *Avi, int entnum, float fvol, float attn, float synctime );
qboolean (*AVI_Think)( struct movie_state_s *Avi );
qboolean (*AVI_SetParm)( struct movie_state_s *Avi, enum movie_parms_e parm, ... );

// glState related calls (must use this instead of normal gl-calls to prevent de-synchornize local states between engine and the client)
void (*GL_Bind)( int tmu, unsigned int texnum );
Expand Down
11 changes: 7 additions & 4 deletions server/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2451,16 +2451,19 @@ unsigned short UTIL_PrecacheMovie( string_t iString, int allow_sound )
unsigned short UTIL_PrecacheMovie( const char *s, int allow_sound )
{
int iCompare;
char path[64], temp[64];
char path[128];

Q_snprintf( path, sizeof( path ), "media/%s", s );
Q_snprintf( temp, sizeof( temp ), "%s%s", allow_sound ? "*" : "", s );
if( Q_snprintf( path, sizeof( path ), "media/%s", s ) < 0 )
{
ALERT( at_console, "Error: video (%s) name too long\n", path );
return 0;
}

// verify file exists
// g-cont. idea! use COMPARE_FILE_TIME instead of LOAD_FILE_FOR_ME
if( COMPARE_FILE_TIME( path, path, &iCompare ))
{
return g_engfuncs.pfnPrecacheGeneric( temp );
return g_engfuncs.pfnPrecacheGeneric( path );
}

ALERT( at_console, "Warning: video (%s) not found!\n", path );
Expand Down
Loading