cvar_t scr_refresh = {0, "scr_refresh", "1", "allows you to completely shut off rendering for benchmarking purposes"};
cvar_t shownetgraph = {CVAR_SAVE, "shownetgraph", "0", "shows a graph of packet sizes and other information, 0 = off, 1 = show client netgraph, 2 = show client and server netgraphs (when hosting a server)"};
+#define AVI_MASTER_INDEX_SIZE 640 // GB ought to be enough for anyone
int jpeg_supported = false;
}
}
-static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunksize, int flags)
+static void GrowBuf(sizebuf_t *buf, int extralen)
{
- if (cls.capturevideo.riffstacklevel != 2)
- Sys_Error("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", cls.capturevideo.riffstacklevel);
- if (cls.capturevideo.riffindexbuffer.cursize + 16 > cls.capturevideo.riffindexbuffer.maxsize)
+ if(buf->cursize + extralen > buf->maxsize)
{
- int oldsize = cls.capturevideo.riffindexbuffer.maxsize;
+ int oldsize = buf->maxsize;
unsigned char *olddata;
- olddata = cls.capturevideo.riffindexbuffer.data;
- cls.capturevideo.riffindexbuffer.maxsize = max(cls.capturevideo.riffindexbuffer.maxsize * 2, 4096);
- cls.capturevideo.riffindexbuffer.data = Mem_Alloc(tempmempool, cls.capturevideo.riffindexbuffer.maxsize);
- if (olddata)
+ olddata = buf->data;
+ buf->maxsize = max(buf->maxsize * 2, 4096);
+ buf->data = Mem_Alloc(tempmempool, buf->maxsize);
+ if(olddata)
{
- memcpy(cls.capturevideo.riffindexbuffer.data, olddata, oldsize);
+ memcpy(buf->data, olddata, oldsize);
Mem_Free(olddata);
}
}
+}
+
+static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunksize, int flags)
+{
+ if (cls.capturevideo.riffstacklevel != 2)
+ Sys_Error("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", cls.capturevideo.riffstacklevel);
+ GrowBuf(&cls.capturevideo.riffindexbuffer, 16);
+ SCR_CaptureVideo_RIFF_Flush();
MSG_WriteUnterminatedString(&cls.capturevideo.riffindexbuffer, chunkfourcc);
MSG_WriteLong(&cls.capturevideo.riffindexbuffer, flags);
MSG_WriteLong(&cls.capturevideo.riffindexbuffer, (int)FS_Tell(cls.capturevideo.videofile) - cls.capturevideo.riffstackstartoffset[1]);
MSG_WriteLong(&cls.capturevideo.riffindexbuffer, chunksize);
}
+static void SCR_CaptureVideo_RIFF_MakeIxChunk(const char *fcc, const char *dwChunkId, fs_offset_t masteridx_counter, int *masteridx_count, fs_offset_t masteridx_start)
+{
+ int nMatching;
+ int i;
+ fs_offset_t ix = SCR_CaptureVideo_RIFF_GetPosition();
+ fs_offset_t pos;
+
+ if(*masteridx_count >= AVI_MASTER_INDEX_SIZE)
+ return;
+
+ nMatching = 0; // go through index and enumerate them
+ for(i = 0; i < cls.capturevideo.riffindexbuffer.cursize; i += 16)
+ if(!memcmp(cls.capturevideo.riffindexbuffer.data + i, dwChunkId, 4))
+ ++nMatching;
+
+ SCR_CaptureVideo_RIFF_Push(fcc, NULL);
+ SCR_CaptureVideo_RIFF_Write16(2); // wLongsPerEntry
+ SCR_CaptureVideo_RIFF_Write16(0x0100); // bIndexType=1, bIndexSubType=0
+ SCR_CaptureVideo_RIFF_Write32(nMatching); // nEntriesInUse
+ SCR_CaptureVideo_RIFF_WriteFourCC(dwChunkId); // dwChunkId
+ SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.videofile_ix_movistart & (fs_offset_t) 0xFFFFFFFFu);
+ SCR_CaptureVideo_RIFF_Write32(((long long) cls.capturevideo.videofile_ix_movistart) >> 32);
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved
+
+ for(i = 0; i < cls.capturevideo.riffindexbuffer.cursize; i += 16)
+ if(!memcmp(cls.capturevideo.riffindexbuffer.data + i, dwChunkId, 4))
+ {
+ unsigned int *p = (unsigned int *) (cls.capturevideo.riffindexbuffer.data + i);
+ unsigned int flags = p[1];
+ unsigned int rpos = p[2];
+ unsigned int size = p[3];
+ size &= ~0x80000000;
+ if(!(flags & 0x10)) // no keyframe?
+ size |= 0x80000000;
+ SCR_CaptureVideo_RIFF_Write32(rpos + 8);
+ SCR_CaptureVideo_RIFF_Write32(size);
+ }
+
+ SCR_CaptureVideo_RIFF_Pop();
+ pos = SCR_CaptureVideo_RIFF_GetPosition();
+ SCR_CaptureVideo_RIFF_Flush();
+
+ FS_Seek(cls.capturevideo.videofile, masteridx_start + 16 * *masteridx_count, SEEK_SET);
+ SCR_CaptureVideo_RIFF_Write32(ix & (fs_offset_t) 0xFFFFFFFFu);
+ SCR_CaptureVideo_RIFF_Write32(((long long) ix) >> 32);
+ SCR_CaptureVideo_RIFF_Write32(pos - ix);
+ SCR_CaptureVideo_RIFF_Write32(nMatching);
+ SCR_CaptureVideo_RIFF_Flush();
+
+ FS_Seek(cls.capturevideo.videofile, masteridx_counter, SEEK_SET);
+ SCR_CaptureVideo_RIFF_Write32(++*masteridx_count);
+ SCR_CaptureVideo_RIFF_Flush();
+
+ FS_Seek(cls.capturevideo.videofile, 0, SEEK_END);
+}
+
static void SCR_CaptureVideo_RIFF_Finish(qboolean final)
{
// close the "movi" list
SCR_CaptureVideo_RIFF_Pop();
- // write the idx1 chunk that we've been building while saving the frames
+ if(cls.capturevideo.videofile_ix_master_video_inuse_offset)
+ SCR_CaptureVideo_RIFF_MakeIxChunk("ix00", "00dc", cls.capturevideo.videofile_ix_master_video_inuse_offset, &cls.capturevideo.videofile_ix_master_video_inuse, cls.capturevideo.videofile_ix_master_video_start_offset);
+ if(cls.capturevideo.videofile_ix_master_audio_inuse_offset)
+ SCR_CaptureVideo_RIFF_MakeIxChunk("ix01", "01wb", cls.capturevideo.videofile_ix_master_audio_inuse_offset, &cls.capturevideo.videofile_ix_master_audio_inuse, cls.capturevideo.videofile_ix_master_audio_start_offset);
+ // write the idx1 chunk that we've been building while saving the frames (for old style players)
if(final && cls.capturevideo.videofile_firstchunkframes_offset)
// TODO replace index creating by OpenDML ix##/##ix/indx chunk so it works for more than one AVI part too
{
// if this would overflow the windows limit of 1GB per RIFF chunk, we need
// to close the current RIFF chunk and open another for future frames
- if (8 + cursize + framesize + cls.capturevideo.riffindexbuffer.cursize + 8 > 1<<30)
+ if (8 + cursize + framesize + cls.capturevideo.riffindexbuffer.cursize + 8 + cls.capturevideo.riffindexbuffer.cursize + 64 > 1<<30) // note that the Ix buffer takes less space... I just don't dare to / 2 here now... sorry, maybe later
{
SCR_CaptureVideo_RIFF_Finish(false);
// begin a new 1GB extended section of the AVI
SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX");
SCR_CaptureVideo_RIFF_Push("LIST", "movi");
+ cls.capturevideo.videofile_ix_movistart = cls.capturevideo.riffstackstartoffset[1];
}
}
SCR_CaptureVideo_RIFF_Write32(0); // color used
SCR_CaptureVideo_RIFF_Write32(0); // color important
SCR_CaptureVideo_RIFF_Pop();
+ // master index
+ SCR_CaptureVideo_RIFF_Push("indx", NULL);
+ SCR_CaptureVideo_RIFF_Write16(4); // wLongsPerEntry
+ SCR_CaptureVideo_RIFF_Write16(0); // bIndexSubType=0, bIndexType=0
+ cls.capturevideo.videofile_ix_master_video_inuse_offset = SCR_CaptureVideo_RIFF_GetPosition();
+ SCR_CaptureVideo_RIFF_Write32(0); // nEntriesInUse
+ SCR_CaptureVideo_RIFF_WriteFourCC("00dc"); // dwChunkId
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved1
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved2
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved3
+ cls.capturevideo.videofile_ix_master_video_start_offset = SCR_CaptureVideo_RIFF_GetPosition();
+ for(i = 0; i < AVI_MASTER_INDEX_SIZE * 4; ++i)
+ SCR_CaptureVideo_RIFF_Write32(0); // fill up later
+ SCR_CaptureVideo_RIFF_Pop();
// extended format (aspect!)
SCR_CaptureVideo_RIFF_Push("vprp", NULL);
SCR_CaptureVideo_RIFF_Write32(0); // VideoFormatToken
SCR_CaptureVideo_RIFF_Write32(0); // VideoStandard
- SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.framerate); // dwVerticalRefreshRate (bogus)
+ SCR_CaptureVideo_RIFF_Write32((int)cls.capturevideo.framerate); // dwVerticalRefreshRate (bogus)
SCR_CaptureVideo_RIFF_Write32(width); // dwHTotalInT
SCR_CaptureVideo_RIFF_Write32(height); // dwVTotalInLines
FindFraction(aspect, &n, &d, 1000);
SCR_CaptureVideo_RIFF_Write16(16); // bits per sample
SCR_CaptureVideo_RIFF_Write16(0); // size
SCR_CaptureVideo_RIFF_Pop();
+ // master index
+ SCR_CaptureVideo_RIFF_Push("indx", NULL);
+ SCR_CaptureVideo_RIFF_Write16(4); // wLongsPerEntry
+ SCR_CaptureVideo_RIFF_Write16(0); // bIndexSubType=0, bIndexType=0
+ cls.capturevideo.videofile_ix_master_audio_inuse_offset = SCR_CaptureVideo_RIFF_GetPosition();
+ SCR_CaptureVideo_RIFF_Write32(0); // nEntriesInUse
+ SCR_CaptureVideo_RIFF_WriteFourCC("01wb"); // dwChunkId
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved1
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved2
+ SCR_CaptureVideo_RIFF_Write32(0); // dwReserved3
+ cls.capturevideo.videofile_ix_master_audio_start_offset = SCR_CaptureVideo_RIFF_GetPosition();
+ for(i = 0; i < AVI_MASTER_INDEX_SIZE * 4; ++i)
+ SCR_CaptureVideo_RIFF_Write32(0); // fill up later
+ SCR_CaptureVideo_RIFF_Pop();
SCR_CaptureVideo_RIFF_Pop();
}
+ cls.capturevideo.videofile_ix_master_audio_inuse = cls.capturevideo.videofile_ix_master_video_inuse = 0;
+
// extended header (for total #frames)
SCR_CaptureVideo_RIFF_Push("LIST", "odml");
SCR_CaptureVideo_RIFF_Push("dmlh", NULL);
SCR_CaptureVideo_RIFF_Pop();
// begin the actual video section now
SCR_CaptureVideo_RIFF_Push("LIST", "movi");
+ cls.capturevideo.videofile_ix_movistart = cls.capturevideo.riffstackstartoffset[1];
// we're done with the headers now...
SCR_CaptureVideo_RIFF_Flush();
if (cls.capturevideo.riffstacklevel != 2)
//=============================================================================
-// LordHavoc: SHOWLMP stuff
-#define SHOWLMP_MAXLABELS 256
-typedef struct showlmp_s
-{
- qboolean isactive;
- float x;
- float y;
- char label[32];
- char pic[128];
-}
-showlmp_t;
-
-showlmp_t showlmp[SHOWLMP_MAXLABELS];
-
void SHOWLMP_decodehide(void)
{
int i;
char *lmplabel;
lmplabel = MSG_ReadString();
- for (i = 0;i < SHOWLMP_MAXLABELS;i++)
- if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
+ for (i = 0;i < cl.num_showlmps;i++)
+ if (cl.showlmps[i].isactive && strcmp(cl.showlmps[i].label, lmplabel) == 0)
{
- showlmp[i].isactive = false;
+ cl.showlmps[i].isactive = false;
return;
}
}
void SHOWLMP_decodeshow(void)
{
- int i, k;
+ int k;
char lmplabel[256], picname[256];
float x, y;
strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
x = MSG_ReadShort();
y = MSG_ReadShort();
}
- k = -1;
- for (i = 0;i < SHOWLMP_MAXLABELS;i++)
- if (showlmp[i].isactive)
- {
- if (strcmp(showlmp[i].label, lmplabel) == 0)
- {
- k = i;
- break; // drop out to replace it
- }
- }
- else if (k < 0) // find first empty one to replace
- k = i;
- if (k < 0)
- return; // none found to replace
- // change existing one
- showlmp[k].isactive = true;
- strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
- strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
- showlmp[k].x = x;
- showlmp[k].y = y;
+ if (!cl.showlmps || cl.num_showlmps >= cl.max_showlmps)
+ {
+ showlmp_t *oldshowlmps = cl.showlmps;
+ cl.max_showlmps += 16;
+ cl.showlmps = Mem_Alloc(cls.levelmempool, cl.max_showlmps * sizeof(showlmp_t));
+ if (cl.num_showlmps)
+ memcpy(cl.showlmps, oldshowlmps, cl.num_showlmps * sizeof(showlmp_t));
+ if (oldshowlmps)
+ Mem_Free(oldshowlmps);
+ }
+ for (k = 0;k < cl.max_showlmps;k++)
+ if (cl.showlmps[k].isactive && !strcmp(cl.showlmps[k].label, lmplabel))
+ break;
+ if (k == cl.max_showlmps)
+ for (k = 0;k < cl.max_showlmps;k++)
+ if (!cl.showlmps[k].isactive)
+ break;
+ cl.showlmps[k].isactive = true;
+ strlcpy (cl.showlmps[k].label, lmplabel, sizeof (cl.showlmps[k].label));
+ strlcpy (cl.showlmps[k].pic, picname, sizeof (cl.showlmps[k].pic));
+ cl.showlmps[k].x = x;
+ cl.showlmps[k].y = y;
+ cl.num_showlmps = max(cl.num_showlmps, k + 1);
}
void SHOWLMP_drawall(void)
{
int i;
- for (i = 0;i < SHOWLMP_MAXLABELS;i++)
- if (showlmp[i].isactive)
- DrawQ_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
-}
-
-void SHOWLMP_clear(void)
-{
- int i;
- for (i = 0;i < SHOWLMP_MAXLABELS;i++)
- showlmp[i].isactive = false;
+ for (i = 0;i < cl.num_showlmps;i++)
+ if (cl.showlmps[i].isactive)
+ DrawQ_Pic(cl.showlmps[i].x, cl.showlmps[i].y, Draw_CachePic(cl.showlmps[i].pic, true), 0, 0, 1, 1, 1, 1, 0);
}
/*
R_Mesh_Start();
if (r_timereport_active)
- R_TimeReport("setup");
+ R_TimeReport("screensetup");
R_UpdateVariables();
+ // Quake uses clockwise winding, so these are swapped
+ r_view.cullface_front = GL_BACK;
+ r_view.cullface_back = GL_FRONT;
+
if (cls.signon == SIGNONS)
{
float size;
qglDepthMask(1);CHECKGLERROR
qglColorMask(1,1,1,1);CHECKGLERROR
qglClearColor(0,0,0,0);CHECKGLERROR
- qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
+ R_ClearScreen();
+ r_view.clear = false;
if(scr_stipple.integer)
{
qglDisable(GL_POLYGON_STIPPLE);
if (r_timereport_active)
- R_TimeReport("clear");
+ R_TimeReport("screenclear");
qglDrawBuffer(GL_BACK);
void CL_Screen_NewMap(void)
{
- SHOWLMP_clear();
}