From afe94889d0a86a7372936cf870bfe2951d449cb8 Mon Sep 17 00:00:00 2001 From: black Date: Sat, 4 Dec 2004 16:15:17 +0000 Subject: [PATCH] -Added the console command prvm_globalset -Rewritten CL_Video to support multiple video streams -Fixed a bug in the new VM's epair parser The only problem CL_Video now has, is that it doesnt use TEXF_FRAGMENT right now. Ive added a more general description of this problem to the todo list. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@4827 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_main.c | 2 - cl_video.c | 362 ++++++++++++++++++++++++++++++++++++--------------- cl_video.h | 60 ++++++++- gl_draw.c | 10 ++ host.c | 2 + prvm_edict.c | 35 ++++- todo | 3 +- 7 files changed, 357 insertions(+), 117 deletions(-) diff --git a/cl_main.c b/cl_main.c index c5caa797..37ad0803 100644 --- a/cl_main.c +++ b/cl_main.c @@ -1381,8 +1381,6 @@ void CL_Init (void) CL_Particles_Init(); CL_Screen_Init(); CL_CGVM_Init(); - - CL_Video_Init(); } diff --git a/cl_video.c b/cl_video.c index 130a58ff..d3804da6 100644 --- a/cl_video.c +++ b/cl_video.c @@ -3,133 +3,283 @@ #include "cl_video.h" #include "dpvsimpledecode.h" -mempool_t *clvideomempool; +// constants (and semi-constants) +static int cl_videormask; +static int cl_videobmask; +static int cl_videogmask; +static int cl_videobytesperpixel; -int cl_videoplaying = false; -void *cl_videostream; +static clvideo_t videoarray[ MAXCLVIDEOS ]; +static mempool_t *cl_videomempool; +static rtexturepool_t *cl_videotexturepool; -double cl_videostarttime; -int cl_videoframenum; -double cl_videoframerate; - -int cl_videoimagewidth; -int cl_videoimageheight; -int cl_videoimagedata_rmask; -int cl_videoimagedata_gmask; -int cl_videoimagedata_bmask; -int cl_videoimagedata_bytesperpixel; -void *cl_videoimagedata; +static clvideo_t *FindUnusedVid( void ) +{ + int i; + for( i = 1 ; i < MAXCLVIDEOS ; i++ ) + if( videoarray[ i ].state == CLVIDEO_UNUSED ) + return &videoarray[ i ]; + return NULL; +} -rtexture_t *cl_videotexture; -rtexturepool_t *cl_videotexturepool; +static qboolean OpenStream( clvideo_t * video ) +{ + char *errorstring; + video->stream = dpvsimpledecode_open( video->filename, &errorstring); + if (!video->stream ) + { + Con_Printf("unable to open \"%s\", error: %s\n", video->filename, errorstring); + return false; + } + return true; +} -void CL_VideoFrame(void) +static void SuspendVideo( clvideo_t * video ) { - int frames, framenum; - if (!cl_videoplaying) + if( video->suspended ) return; - framenum = (realtime - cl_videostarttime) * cl_videoframerate; - //Con_Printf("frame %i\n", framenum); - if (framenum < 0) - framenum = 0; - frames = 0; - while (cl_videoframenum < framenum) - { - frames++; - cl_videoframenum++; - if (dpvsimpledecode_video(cl_videostream, cl_videoimagedata, cl_videoimagedata_rmask, cl_videoimagedata_gmask, cl_videoimagedata_bmask, cl_videoimagedata_bytesperpixel, cl_videoimagewidth * cl_videoimagedata_bytesperpixel)) - { - CL_VideoStop(); - return; + video->suspended = true; + // free the texture + R_FreeTexture( video->cpif.tex ); + // free the image data + Mem_Free( video->imagedata ); + // if we are in firstframe mode, also close the stream + if( video->state == CLVIDEO_FIRSTFRAME ) + dpvsimpledecode_close( video->stream ); +} + +static qboolean WakeVideo( clvideo_t * video ) +{ + if( !video->suspended ) + return true; + video->suspended = false; + + if( video->state == CLVIDEO_FIRSTFRAME ) + if( !OpenStream( video ) ) { + video->state = CLVIDEO_UNUSED; + return false; } + + video->imagedata = Mem_Alloc( cl_videomempool, video->cpif.width * video->cpif.height * cl_videobytesperpixel ); + video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name, + video->cpif.width, video->cpif.height, NULL, TEXTYPE_RGBA, 0, NULL ); + + // update starttime + video->starttime += realtime - video->lasttime; + return true; +} + +static clvideo_t* OpenVideo( clvideo_t *video, char *filename, char *name, int owner ) +{ + strncpy( video->filename, filename, MAX_QPATH ); + video->ownertag = owner; + strncpy( video->cpif.name, CLVIDEOPREFIX, MAX_QPATH ); + strncat( video->cpif.name, name, MAX_QPATH - sizeof( CLVIDEOPREFIX ) ); + + if( !OpenStream( video ) ) + return NULL; + + video->state = CLVIDEO_FIRSTFRAME; + video->framenum = -1; + video->framerate = dpvsimpledecode_getframerate( video->stream ); + video->lasttime = realtime; + + video->cpif.width = dpvsimpledecode_getwidth( video->stream ); + video->cpif.height = dpvsimpledecode_getheight( video->stream ); + video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name, + video->cpif.width, video->cpif.height, NULL, TEXTYPE_RGBA, 0, NULL ); + + video->imagedata = Mem_Alloc( cl_videomempool, video->cpif.width * video->cpif.height * cl_videobytesperpixel ); + + return video; +} + +clvideo_t* CL_OpenVideo( char *filename, char *name, int owner ) +{ + clvideo_t *video; + + video = FindUnusedVid(); + if( !video ) { + Con_Printf( "unable to open video \"%s\" - video limit reached\n", filename ); + return NULL; } - if (frames) - { - R_UpdateTexture(cl_videotexture, cl_videoimagedata); - //Draw_NewPic("engine_videoframe", cl_videoimagewidth, cl_videoimageheight, false, cl_videoimagedata); + return OpenVideo( video, filename, name, owner ); +} + +clvideo_t* CL_GetVideo( char *name ) +{ + int i; + clvideo_t *video; + + for( i = 0 ; i < MAXCLVIDEOS ; i++ ) + if( videoarray[ i ].state != CLVIDEO_UNUSED + && !strcmp( videoarray[ i ].cpif.name , name ) ) + break; + if( i == MAXCLVIDEOS ) + return NULL; + video = &videoarray[ i ]; + + if( video->suspended ) + if( !WakeVideo( video ) ) + return NULL; + video->lasttime = realtime; + + return video; +} + +void CL_StartVideo( clvideo_t * video ) +{ + if( !video ) + return; + + video->starttime = video->lasttime = realtime; + video->framenum = -1; + video->state = CLVIDEO_PLAY; +} + +void CL_LoopVideo( clvideo_t * video ) +{ + if( !video ) + return; + + video->starttime = video->lasttime = realtime; + video->framenum = -1; + video->state = CLVIDEO_LOOP; +} + +void CL_PauseVideo( clvideo_t * video ) +{ + if( !video ) + return; + + video->state = CLVIDEO_PAUSE; + video->lasttime = realtime; +} + +void CL_RestartVideo( clvideo_t *video ) +{ + if( !video ) + return; + + video->starttime = video->lasttime = realtime; + video->framenum = -1; +} + +void CL_StopVideo( clvideo_t * video ) +{ + if( !video ) + return; + + video->lasttime = realtime; + video->framenum = -1; + video->state = CLVIDEO_FIRSTFRAME; +} + +void CL_CloseVideo( clvideo_t * video ) +{ + if( !video || video->state == CLVIDEO_UNUSED ) + return; + + video->state = CLVIDEO_UNUSED; + + if( !video->suspended || video->state != CLVIDEO_FIRSTFRAME ) + dpvsimpledecode_close( video->stream ); + if( !video->suspended ) { + Mem_Free( video->imagedata ); + R_FreeTexture( video->cpif.tex ); } } +static void VideoFrame( clvideo_t *video ) +{ + int destframe; + + if( video->state == CLVIDEO_FIRSTFRAME ) + destframe = 0; + else + destframe = (realtime - video->starttime) * video->framerate; + if( destframe < 0 ) + destframe = 0; + if( video->framenum < destframe ) { + do { + video->framenum++; + if( dpvsimpledecode_video( video->stream, video->imagedata, cl_videormask, + cl_videogmask, cl_videobmask, cl_videobytesperpixel, + cl_videobytesperpixel * video->cpif.width ) + ) { // finished? + video->framenum = -1; + if( video->state == CLVIDEO_LOOP ) + video->starttime = realtime; + else if( video->state == CLVIDEO_PLAY ) + video->state = CLVIDEO_FIRSTFRAME; + return; + } + } while( video->framenum < destframe ); + R_UpdateTexture( video->cpif.tex, video->imagedata ); + } +} + +void CL_VideoFrame( void ) // update all videos +{ + int i; + clvideo_t *video; + + for( video = videoarray, i = 0 ; i < MAXCLVIDEOS ; video++, i++ ) + if( video->state != CLVIDEO_UNUSED && !video->suspended ) + if( realtime - video->lasttime > CLTHRESHOLD ) + SuspendVideo( video ); + else if( video->state == CLVIDEO_PAUSE ) + video->starttime = realtime + video->framenum * video->framerate; + else + VideoFrame( video ); + + if( videoarray->state == CLVIDEO_FIRSTFRAME ) + CL_VideoStop(); +} + +void CL_Video_Shutdown( void ) +{ + int i; + for( i = 0 ; i < MAXCLVIDEOS ; i++ ) + CL_CloseVideo( &videoarray[ i ] ); + + R_FreeTexturePool( &cl_videotexturepool ); + Mem_FreePool( &cl_videomempool ); +} + +void CL_PurgeOwner( int owner ) +{ + int i; + for( i = 0 ; i < MAXCLVIDEOS ; i++ ) + if( videoarray[ i ].ownertag == owner ) + CL_CloseVideo( &videoarray[ i ] ); +} + +int cl_videoplaying = false; // old, but still supported + void CL_DrawVideo(void) { if (cl_videoplaying) - { - drawqueuemesh_t mesh; - float vertex3f[12]; - float texcoord2f[8]; - float color4f[16]; - float s1, t1, s2, t2, x1, y1, x2, y2; - x1 = 0; - y1 = 0; - x2 = vid.conwidth; - y2 = vid.conheight; - R_FragmentLocation(cl_videotexture, NULL, NULL, &s1, &t1, &s2, &t2); - texcoord2f[0] = s1;texcoord2f[1] = t1; - texcoord2f[2] = s2;texcoord2f[3] = t1; - texcoord2f[4] = s2;texcoord2f[5] = t2; - texcoord2f[6] = s1;texcoord2f[7] = t2; - R_FillColors(color4f, 4, 1, 1, 1, 1); - vertex3f[ 0] = x1;vertex3f[ 1] = y1;vertex3f[ 2] = 0; - vertex3f[ 3] = x2;vertex3f[ 4] = y1;vertex3f[ 5] = 0; - vertex3f[ 6] = x2;vertex3f[ 7] = y2;vertex3f[ 8] = 0; - vertex3f[ 9] = x1;vertex3f[10] = y2;vertex3f[11] = 0; - mesh.texture = cl_videotexture; - mesh.num_triangles = 2; - mesh.num_vertices = 4; - mesh.data_element3i = polygonelements; - mesh.data_vertex3f = vertex3f; - mesh.data_texcoord2f = texcoord2f; - mesh.data_color4f = color4f; - DrawQ_Mesh(&mesh, 0); - //DrawQ_Pic(0, 0, "engine_videoframe", vid.conwidth, vid.conheight, 1, 1, 1, 1, 0); - } + DrawQ_Pic(0, 0, videoarray->cpif.name, vid.conwidth, vid.conheight, 1, 1, 1, 1, 0); } void CL_VideoStart(char *filename) { - char *errorstring; - cl_videostream = dpvsimpledecode_open(filename, &errorstring); - if (!cl_videostream) - { - Con_Printf("unable to open \"%s\", error: %s\n", filename, errorstring); + if( videoarray->state != CLVIDEO_UNUSED ) + CL_CloseVideo( videoarray ); + if( !OpenVideo( videoarray, filename, filename, 0 ) ) return; - } cl_videoplaying = true; - cl_videostarttime = realtime; - cl_videoframenum = -1; - cl_videoframerate = dpvsimpledecode_getframerate(cl_videostream); - cl_videoimagewidth = dpvsimpledecode_getwidth(cl_videostream); - cl_videoimageheight = dpvsimpledecode_getheight(cl_videostream); - - // RGBA format - cl_videoimagedata_bytesperpixel = 4; - cl_videoimagedata_rmask = BigLong(0xFF000000); - cl_videoimagedata_gmask = BigLong(0x00FF0000); - cl_videoimagedata_bmask = BigLong(0x0000FF00); - cl_videoimagedata = Mem_Alloc(clvideomempool, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel); - //memset(cl_videoimagedata, 97, cl_videoimagewidth * cl_videoimageheight * cl_videoimagedata_bytesperpixel); - cl_videotexturepool = R_AllocTexturePool(); - cl_videotexture = R_LoadTexture2D(cl_videotexturepool, "videotexture", cl_videoimagewidth, cl_videoimageheight, NULL, TEXTYPE_RGBA, TEXF_FRAGMENT, NULL); + CL_StartVideo( videoarray ); } void CL_VideoStop(void) { cl_videoplaying = false; - if (cl_videostream) - dpvsimpledecode_close(cl_videostream); - cl_videostream = NULL; - - if (cl_videoimagedata) - Mem_Free(cl_videoimagedata); - cl_videoimagedata = NULL; - - cl_videotexture = NULL; - R_FreeTexturePool(&cl_videotexturepool); - - Draw_FreePic("engine_videoframe"); + CL_CloseVideo( videoarray ); } static void CL_PlayVideo_f(void) @@ -151,10 +301,16 @@ static void CL_StopVideo_f(void) CL_VideoStop(); } -void CL_Video_Init(void) +void CL_Video_Init( void ) { - Cmd_AddCommand("playvideo", CL_PlayVideo_f); - Cmd_AddCommand("stopvideo", CL_StopVideo_f); + cl_videobytesperpixel = 4; + cl_videormask = BigLong(0xFF000000); + cl_videogmask = BigLong(0x00FF0000); + cl_videobmask = BigLong(0x0000FF00); + + cl_videomempool = Mem_AllocPool( "CL_Video", 0, NULL ); + cl_videotexturepool = R_AllocTexturePool(); - clvideomempool = Mem_AllocPool("CL_Video", 0, NULL); + Cmd_AddCommand( "playvideo", CL_PlayVideo_f ); + Cmd_AddCommand( "stopvideo", CL_StopVideo_f ); } diff --git a/cl_video.h b/cl_video.h index 58738afc..9a6ad8c1 100644 --- a/cl_video.h +++ b/cl_video.h @@ -2,11 +2,61 @@ #ifndef CL_VIDEO_H #define CL_VIDEO_H +#define MAXCLVIDEOS 64 + 1 // 1 video is reserved for the cinematic mode +#define CLVIDEOPREFIX "_" +#define CLTHRESHOLD 2.0 + +typedef enum clvideostate_s +{ + CLVIDEO_UNUSED, + CLVIDEO_PLAY, + CLVIDEO_LOOP, + CLVIDEO_PAUSE, + CLVIDEO_FIRSTFRAME, +} clvideostate_t; + +typedef struct clvideo_s +{ + int ownertag; + clvideostate_t state; + + // private stuff + void *stream; + + double starttime; + int framenum; + double framerate; + + void *imagedata; + + cachepic_t cpif; + + // if a video is suspended, it is automatically paused (else we'd still have to process the frames) + double lasttime; // used to determine whether the video's resources should be freed or not + qboolean suspended; // when lasttime - realtime > THRESHOLD, all but the stream is freed + + char filename[MAX_QPATH]; +} clvideo_t; + +clvideo_t* CL_OpenVideo( char *filename, char *name, int owner ); +clvideo_t* CL_GetVideo( char *name ); +void CL_StartVideo( clvideo_t * video ); +void CL_LoopVideo( clvideo_t * video ); +void CL_PauseVideo( clvideo_t * video ); +void CL_StopVideo( clvideo_t * video ); +void CL_RestartVideo( clvideo_t *video ); +void CL_CloseVideo( clvideo_t * video ); +void CL_PurgeOwner( int owner ); + +void CL_VideoFrame( void ); // update all videos +void CL_Video_Init( void ); +void CL_Video_Shutdown( void ); + +// old interface extern int cl_videoplaying; -void CL_VideoFrame(void); -void CL_DrawVideo(void); -void CL_VideoStart(char *filename); -void CL_VideoStop(void); -void CL_Video_Init(void); + +void CL_DrawVideo( void ); +void CL_VideoStart( char *filename ); +void CL_VideoStop( void ); #endif diff --git a/gl_draw.c b/gl_draw.c index e63b7d4b..9c801c43 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "image.h" #include "wad.h" +#include "cl_video.h" + static rtexture_t *char_texture; @@ -272,6 +274,14 @@ cachepic_t *Draw_CachePic (char *path) if (!strcmp (path, pic->name)) return pic; + if (!strncmp(CLVIDEOPREFIX, path, sizeof(CLVIDEOPREFIX) - 1)) { + clvideo_t *video; + + video = CL_GetVideo(path); + if( video ) + return &video->cpif; + } + if (numcachepics == MAX_CACHED_PICS) Sys_Error ("numcachepics == MAX_CACHED_PICS"); pic = cachepics + (numcachepics++); diff --git a/host.c b/host.c index e6b20840..c7fe6ac0 100644 --- a/host.c +++ b/host.c @@ -942,6 +942,7 @@ void Host_Init (void) if (cls.state != ca_dedicated) { VID_Open(); + CL_Video_Init(); CL_InitTEnts (); // We must wait after sound startup to load tent sounds SCR_BeginLoadingPlaque(); MR_Init(); @@ -1009,6 +1010,7 @@ void Host_Shutdown(void) // AK shutdown PRVM // AK hmm, no PRVM_Shutdown(); yet + CL_Video_Shutdown(); Host_SaveConfig_f(); diff --git a/prvm_edict.c b/prvm_edict.c index d3c5bbd2..c7d6b6b5 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -915,7 +915,7 @@ qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s) if (ent) val = (prvm_eval_t *)((int *)ent->v + key->ofs); else - val = (prvm_eval_t *)((int *)pr_globals + key->ofs); + val = (prvm_eval_t *)((int *)prog->globals + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: @@ -1013,12 +1013,9 @@ void PRVM_ED_EdictSet_f(void) ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2))); if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0) - { Con_Printf("Key %s not found !\n", Cmd_Argv(3)); - return; - } - - PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4)); + else + PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4)); PRVM_End; } @@ -1679,6 +1676,31 @@ void PRVM_Global_f(void) PRVM_End; } +/* +=============== +PRVM_GlobalSet +=============== +*/ +void PRVM_GlobalSet_f(void) +{ + ddef_t *global; + if( Cmd_Argc() != 4 ) { + Con_Printf( "prvm_globalset \n" ); + return; + } + + PRVM_Begin; + if( !PRVM_SetProgFromString( Cmd_Argv(1) ) ) + return; + + global = PRVM_ED_FindGlobal( Cmd_Argv(2) ); + if( !global ) + Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) ); + else + PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) ); + PRVM_End; +} + /* =============== PRVM_Init @@ -1693,6 +1715,7 @@ void PRVM_Init (void) Cmd_AddCommand ("prvm_fields", PRVM_Fields_f); Cmd_AddCommand ("prvm_globals", PRVM_Globals_f); Cmd_AddCommand ("prvm_global", PRVM_Global_f); + Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f); Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f); // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others)) Cvar_RegisterVariable (&prvm_boundscheck); diff --git a/todo b/todo index 739280ea..cb8230f1 100644 --- a/todo +++ b/todo @@ -185,6 +185,7 @@ 1 lhfire: get lhfire_gui build from Tomaz. 1 lhfire: post lhfire_gui build. 1 lhfire: prepare example scripts for release. +2 darkplaces client: make CL_Video use TEXF_FRAGMENT again by adding general, transparent support for it in all drawqueue functions (so you dont need to call FragmentLocation) (Black) 2 darkplaces cleanup: add cvar callbacks and make net cvars have callbacks 2 darkplaces cleanup: add fs_datapath and fs_userpath cvars to better support Linux, this can be done by making each gamedir add both the basepath and userpath variants of the gamedir, and making sure the userpath one is last so it is used for writing (Mercury) 2 darkplaces cleanup: change menu qc key input to using string key names instead of numbers (the bind alias names should be able to do this) (Mercury, Black, Vermeulen) @@ -301,7 +302,7 @@ 4 darkplaces cleanup: use the memory pool nesting feature ! (Black[,Vicious]) 4 darkplaces client: add decals on models (Urre) 4 darkplaces client: add qw protocol support (making darkplaces work as a qwcl client) (tell Fuh) -4 darkplaces client: add video playback handles to the cl_video code so that other systems can use streaming video textures, and allow the menu qc to use these (Black) +d darkplaces client: add video playback handles to the cl_video code so that other systems can use streaming video textures, and allow the menu qc to use these (Black) 4 darkplaces client: figure out why intermission camera pitch changes after a moment (Tomaz) 4 darkplaces console: add setlock command which marks a cvar as locked, and sends it over network to connected clients as a setlock command, the clients will not allow the user to modify the cvars while locked (and will only accept setlock commands over the network), and cvars are unlocked when level ends - the server will send the locks again on next level (VorteX) 4 darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh) -- 2.39.2