diff --git a/include/QF/screen.h b/include/QF/screen.h index 56fb74361..3853cdb1a 100644 --- a/include/QF/screen.h +++ b/include/QF/screen.h @@ -56,6 +56,7 @@ void SCR_DrawTurtle (void); void SCR_DrawPause (void); struct tex_s *SCR_ScreenShot (int width, int height); +struct tex_s *SCR_CaptureBGR (void); void SCR_DrawStringToSnap (const char *s, struct tex_s *tex, int x, int y); int MipColor (int r, int g, int b); int SCR_ModalMessage (const char *text); diff --git a/libs/video/renderer/gl/gl_screen.c b/libs/video/renderer/gl/gl_screen.c index 7ed310786..0c7ee9671 100644 --- a/libs/video/renderer/gl/gl_screen.c +++ b/libs/video/renderer/gl/gl_screen.c @@ -28,8 +28,7 @@ # include "config.h" #endif -static __attribute__ ((used)) const char rcsid[] = - "$Id$"; +static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include @@ -66,6 +65,24 @@ static __attribute__ ((used)) const char rcsid[] = /* SCREEN SHOTS */ +VISIBLE tex_t * +SCR_CaptureBGR (void) +{ + int count; + tex_t *tex; + + count = vid.width * vid.height; + tex = malloc (field_offset (tex_t, data[count * 3])); + SYS_CHECKMEM (tex); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + qfglReadPixels (0, 0, tex->width, tex->height, GL_BGR_EXT, + GL_UNSIGNED_BYTE, tex->data); + return tex; +} + VISIBLE tex_t * SCR_ScreenShot (int width, int height) { @@ -133,20 +150,18 @@ SCR_ScreenShot (int width, int height) void SCR_ScreenShot_f (void) { - byte *buffer; dstring_t *pcxname = dstring_new (); - // find a file name to save it to + // find a file name to save it to if (!QFS_NextFilename (pcxname, va ("%s/qf", qfs_gamedir->dir.shots), ".tga")) { Sys_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); } else { - buffer = malloc (vid.width * vid.height * 3); - SYS_CHECKMEM (buffer); - qfglReadPixels (0, 0, vid.width, vid.height, GL_BGR_EXT, - GL_UNSIGNED_BYTE, buffer); - WriteTGAfile (pcxname->str, buffer, vid.width, vid.height); - free (buffer); + tex_t *tex; + + tex = SCR_CaptureBGR (); + WriteTGAfile (pcxname->str, tex->data, tex->width, tex->height); + free (tex); Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str); } dstring_delete (pcxname); diff --git a/libs/video/renderer/glsl/glsl_screen.c b/libs/video/renderer/glsl/glsl_screen.c index d03ae83bf..2505bf2b0 100644 --- a/libs/video/renderer/glsl/glsl_screen.c +++ b/libs/video/renderer/glsl/glsl_screen.c @@ -203,6 +203,31 @@ SCR_UpdateScreen (double realtime, SCR_Func *scr_funcs) qfglFlush (); } +VISIBLE tex_t * +SCR_CaptureBGR (void) +{ + byte *r, *b; + int count, i; + tex_t *tex; + + count = vid.width * vid.height; + tex = malloc (field_offset (tex_t, data[count * 3])); + SYS_CHECKMEM (tex); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + qfglReadPixels (0, 0, vid.width, vid.height, GL_RGB, + GL_UNSIGNED_BYTE, tex->data); + for (i = 0, r = tex->data, b = tex->data + 2; i < count; + i++, r += 3, b += 3) { + byte t = *b; + *b = *r; + *r = t; + } + return tex; +} + VISIBLE tex_t * SCR_ScreenShot (int width, int height) { @@ -212,28 +237,18 @@ SCR_ScreenShot (int width, int height) VISIBLE void SCR_ScreenShot_f (void) { - byte *buffer, *r, *b; dstring_t *name = dstring_new (); - int size, i; // find a file name to save it to if (!QFS_NextFilename (name, va ("%s/qf", qfs_gamedir->dir.shots), ".png")) { Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file\n"); } else { - size = vid.width * vid.height; - buffer = malloc (size * 3); - SYS_CHECKMEM (buffer); - qfglReadPixels (0, 0, vid.width, vid.height, GL_RGB, - GL_UNSIGNED_BYTE, buffer); - // FIXME have to swap rgb (WritePNG bug?) - for (i = 0, r = buffer, b = buffer + 2; i < size; i++, r+=3, b+=3) { - byte t = *b; - *b = *r; - *r = t; - } - WritePNGqfs (name->str, buffer, vid.width, vid.height); - free (buffer); + tex_t *tex; + + tex = SCR_CaptureBGR (); + WritePNGqfs (name->str, tex->data, tex->width, tex->height); + free (tex); Sys_Printf ("Wrote %s/%s\n", qfs_userpath, name->str); } dstring_delete (name); diff --git a/libs/video/renderer/sw/screen.c b/libs/video/renderer/sw/screen.c index 010606a1e..46b57ee1d 100644 --- a/libs/video/renderer/sw/screen.c +++ b/libs/video/renderer/sw/screen.c @@ -28,8 +28,7 @@ # include "config.h" #endif -static __attribute__ ((used)) const char rcsid[] = - "$Id$"; +static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include @@ -87,6 +86,36 @@ SCR_ApplyBlend (void) // Used to be V_UpdatePalette /* SCREEN SHOTS */ +VISIBLE tex_t * +SCR_CaptureBGR (void) +{ + int count, x, y; + tex_t *tex; + const byte *src; + byte *dst; + + count = vid.width * vid.height; + tex = malloc (field_offset (tex_t, data[count * 3])); + SYS_CHECKMEM (tex); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + D_EnableBackBufferAccess (); + src = vid.buffer; + for (y = 0; y < tex->height; y++) { + dst = tex->data + (tex->height - 1 - y) * tex->width * 3; + for (x = 0; x < tex->width; x++) { + *dst++ = vid.basepal[*src * 3 + 2]; // blue + *dst++ = vid.basepal[*src * 3 + 1]; // green + *dst++ = vid.basepal[*src * 3 + 0]; // red + src++; + } + } + D_DisableBackBufferAccess (); + return tex; +} + tex_t * SCR_ScreenShot (int width, int height) { @@ -159,7 +188,7 @@ SCR_ScreenShot_f (void) pcx_t *pcx; int pcx_len; - // find a file name to save it to + // find a file name to save it to if (!QFS_NextFilename (pcxname, va ("%s/qf", qfs_gamedir->dir.shots), ".pcx")) { Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); diff --git a/libs/video/renderer/sw32/screen.c b/libs/video/renderer/sw32/screen.c index 5aa6d187d..58d04cae1 100644 --- a/libs/video/renderer/sw32/screen.c +++ b/libs/video/renderer/sw32/screen.c @@ -28,8 +28,7 @@ # include "config.h" #endif -static __attribute__ ((used)) const char rcsid[] = - "$Id$"; +static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include @@ -155,6 +154,36 @@ SCR_ApplyBlend (void) // Used to be V_UpdatePalette /* SCREEN SHOTS */ +VISIBLE tex_t * +SCR_CaptureBGR (void) +{ + int count, x, y; + tex_t *tex; + const byte *src; + byte *dst; + + count = vid.width * vid.height; + tex = malloc (field_offset (tex_t, data[count * 3])); + SYS_CHECKMEM (tex); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + D_EnableBackBufferAccess (); + src = vid.buffer; + for (y = 0; y < tex->height; y++) { + dst = tex->data + (tex->height - 1 - y) * tex->width * 3; + for (x = 0; x < tex->width; x++) { + *dst++ = vid.basepal[*src * 3 + 2]; // blue + *dst++ = vid.basepal[*src * 3 + 1]; // green + *dst++ = vid.basepal[*src * 3 + 0]; // red + src++; + } + } + D_DisableBackBufferAccess (); + return tex; +} + VISIBLE tex_t * SCR_ScreenShot (int width, int height) { @@ -168,7 +197,7 @@ SCR_ScreenShot_f (void) pcx_t *pcx = 0; int pcx_len; - // find a file name to save it to + // find a file name to save it to if (!QFS_NextFilename (pcxname, va ("%s/qf", qfs_gamedir->dir.shots), ".pcx")) { Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); diff --git a/nq/include/client.h b/nq/include/client.h index 183be650c..249c53fab 100644 --- a/nq/include/client.h +++ b/nq/include/client.h @@ -119,6 +119,7 @@ typedef struct // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) qboolean demorecording; + qboolean demo_capture; qboolean demoplayback; qboolean timedemo; int forcetrack; // -1 = use normal cd track diff --git a/nq/source/cl_demo.c b/nq/source/cl_demo.c index cbcef2c38..1511de1c6 100644 --- a/nq/source/cl_demo.c +++ b/nq/source/cl_demo.c @@ -126,6 +126,7 @@ CL_StopPlayback (void) Qclose (cls.demofile); cls.demofile = NULL; CL_SetState (ca_disconnected); + cls.demo_capture = 0; cls.demoplayback = 0; key_game_target = IMT_0; Key_SetKeyDest (key_game); @@ -384,9 +385,19 @@ CL_PlayDemo_f (void) if (cmd_source != src_command) return; - if (Cmd_Argc () != 2) { - Sys_Printf ("play : plays a demo\n"); - return; + switch (Cmd_Argc ()) { + case 2: + cls.demo_capture = 0; + break; + case 3: + if (!strcmp (Cmd_Argv (2), "rec")) { + cls.demo_capture = 1; + break; + } + // fall through + default: + Sys_Printf ("play : plays a demo\n"); + return; } timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); diff --git a/nq/source/host.c b/nq/source/host.c index c86a1a40f..d8279fe25 100644 --- a/nq/source/host.c +++ b/nq/source/host.c @@ -41,12 +41,15 @@ static __attribute__ ((used)) const char rcsid[] = "$Id$"; #include "QF/cmd.h" #include "QF/console.h" #include "QF/cvar.h" +#include "QF/image.h" #include "QF/input.h" #include "QF/keys.h" #include "QF/msg.h" #include "QF/plugin.h" +#include "QF/png.h" #include "QF/progs.h" #include "QF/qargs.h" +#include "QF/screen.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/vid.h" @@ -638,6 +641,9 @@ _Host_Frame (float time) rand (); // keep the random time dependent + if (cls.demo_capture) + time = 1.0 / 30; //FIXME fixed 30fps atm + // decide the simulation time if ((sleeptime = Host_FilterTime (time)) != 0) { // don't run too fast, or packet will flood outs @@ -685,6 +691,14 @@ _Host_Frame (float time) else host_time += host_frametime; //FIXME is this needed? vcr stuff + if (cls.demo_capture) { + tex_t *tex = SCR_CaptureBGR (); + WritePNGqfs (va ("%s/qfmv%06d.png", qfs_gamedir->dir.shots, + cls.demo_capture++), + tex->data, tex->width, tex->height); + free (tex); + } + host_framecount++; fps_count++; } diff --git a/nq/source/sv_ded.c b/nq/source/sv_ded.c index 45b248bcb..02e5afa16 100644 --- a/nq/source/sv_ded.c +++ b/nq/source/sv_ded.c @@ -33,6 +33,7 @@ static __attribute__ ((used)) const char rcsid[] = "$Id$"; #include "QF/cdaudio.h" #include "QF/cvar.h" #include "QF/plugin.h" +#include "QF/screen.h" #include "host.h" #include "server.h" @@ -70,6 +71,13 @@ CL_UpdateScreen (double realtime) { } +struct tex_s * +SCR_CaptureBGR (void) +{ + return 0; +} + + void CL_Cmd_ForwardToServer (void) { diff --git a/qw/include/client.h b/qw/include/client.h index 837b6dfc9..ffaae3c54 100644 --- a/qw/include/client.h +++ b/qw/include/client.h @@ -167,6 +167,7 @@ typedef struct // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) qboolean demorecording; + qboolean demo_capture; qboolean demoplayback; qboolean demoplayback2; qboolean findtrack; diff --git a/qw/source/cl_demo.c b/qw/source/cl_demo.c index 4af44cffc..5258932c0 100644 --- a/qw/source/cl_demo.c +++ b/qw/source/cl_demo.c @@ -144,6 +144,7 @@ CL_StopPlayback (void) Qclose (cls.demofile); cls.demofile = NULL; CL_SetState (ca_disconnected); + cls.demo_capture = 0; cls.demoplayback = 0; cls.demoplayback2 = 0; demotime_cached = 0; @@ -933,9 +934,19 @@ CL_StartDemo (void) static void CL_PlayDemo_f (void) { - if (Cmd_Argc () != 2) { - Sys_Printf ("play : plays a demo\n"); - return; + switch (Cmd_Argc ()) { + case 2: + cls.demo_capture = 0; + break; + case 3: + if (!strcmp (Cmd_Argv (2), "rec")) { + cls.demo_capture = 1; + break; + } + // fall through + default: + Sys_Printf ("play : plays a demo\n"); + return; } timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop // disconnect from server diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c index 0f43028ae..61d4c67dc 100644 --- a/qw/source/cl_main.c +++ b/qw/source/cl_main.c @@ -71,11 +71,13 @@ static __attribute__ ((used)) const char rcsid[] = #include "QF/console.h" #include "QF/cvar.h" #include "QF/draw.h" +#include "QF/image.h" #include "QF/input.h" #include "QF/keys.h" #include "QF/model.h" #include "QF/msg.h" #include "QF/plugin.h" +#include "QF/png.h" #include "QF/progs.h" #include "QF/qargs.h" #include "QF/qendian.h" @@ -1488,7 +1490,7 @@ Host_SimulationTime (float time) if (oldrealtime > realtime) oldrealtime = 0; - if (cls.timedemo) + if (cls.demoplayback) return 0; if (cl_maxfps->value <= 0) @@ -1523,6 +1525,9 @@ Host_Frame (float time) // something bad happened, or the server disconnected return; + if (cls.demo_capture) + time = 1.0 / 30; //FIXME fixed 30fps atm + // decide the simulation time if ((sleeptime = Host_SimulationTime (time)) != 0) { #ifdef HAVE_USLEEP @@ -1623,6 +1628,14 @@ Host_Frame (float time) pass1 + pass2 + pass3, pass1, pass2, pass3); } + if (cls.demo_capture) { + tex_t *tex = SCR_CaptureBGR (); + WritePNGqfs (va ("%s/qfmv%06d.png", qfs_gamedir->dir.shots, + cls.demo_capture++), + tex->data, tex->width, tex->height); + free (tex); + } + host_framecount++; fps_count++; }