r00k wrote:qBISM UR ICON STILL SCARES ME.
It's a warning, stay outta my swamp!
Actually a screenshot, DP w/ retexture and high contrast. Now, this is scary-
First attempt at saving a snapshot (EDIT: that is, a snapshot within demo recording) of all particles in their current state. Works perfectly except that it doesn't work. All the particles are saved and appear to be parsed correctly during playback, but after rebuilding the particles they just don't show up. One possibility is an error in 'die' time parsing that snuffs particles immediately. Another is some aspect of demo message composure and/or playback that I'm not understanding.
I created 'svc_particledemo' and added this function to cl_demo.c
Code: Select all
void CL_UpdateDemoParticle (particle_t *p) //qbism
{
int i;
MSG_WriteByte (&net_message, svc_particledemo);
for (i=0 ; i<3 ; i++)
MSG_WriteCoord (&net_message, p->org[i]);
for (i=0 ; i<3 ; i++)
MSG_WriteCoord (&net_message, p->vel[i]);
MSG_WriteFloat(&net_message, p->color);
MSG_WriteByte(&net_message, (byte)p->type);
MSG_WriteFloat(&net_message, p->ramp);
MSG_WriteFloat(&net_message, p->die - cl.time); //qbism - add back cl.time when parsing
MSG_WriteFloat(&net_message, cl.time - p->start_time);
MSG_WriteFloat(&net_message, p->alpha);
MSG_WriteFloat(&net_message, p->alphavel);
}
CL_UpdateDemoParticle is called from CL_Record_f via a loop that runs through all the existing particles. The whole function is pasted here, along with ProQuake 'record any time' modifications. Sorry for any resulting scrolling fatigue but the context is needed. The interesting part starts about 3/4 down, at 'for (p=active_particles ; p ; p=p->next)'
Code: Select all
void CL_Record_f (void)
{
int c;
char name[MAX_OSPATH];
int track;
particle_t *p, *pnext; //qbism - send particles
if (cmd_source != src_command)
return;
c = Cmd_Argc();
if (c != 2 && c != 3 && c != 4)
{
Con_Printf ("record <demoname> [<map> [cd track]]\n");
return;
}
if (strstr(Cmd_Argv(1), ".."))
{
Con_Printf ("Relative pathnames are not allowed.\n");
return;
}
// JPG 3.00 - consecutive demo bug
if (cls.demorecording)
CL_Stop_f();
/* JPG 1.05 - got rid of this because recording after connecting is now supported
if (c == 2 && cls.state == ca_connected)
{
Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
return;
}
*/
// JPG 1.05 - replaced it with this
if (c == 2 && cls.state == ca_connected && cls.signon < 2)
{
Con_Printf("Can't record - try again when connected\n");
return;
}
// write the forced cd track number, or -1
if (c == 4)
{
track = atoi(Cmd_Argv(3));
Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
}
else
track = -1;
sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
//
// start the map up
//
if (c > 2)
Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
//
// open the demo file
//
COM_DefaultExtension (name, ".dem");
Con_Printf ("recording to %s.\n", name);
cls.demofile = fopen (name, "wb");
if (!cls.demofile)
{
Con_Printf ("ERROR: couldn't open.\n");
return;
}
cls.forcetrack = track;
fprintf (cls.demofile, "%i\n", cls.forcetrack);
cls.demorecording = true;
// JPG 1.05 - initialize the demo file if we're already connected
if (c == 2 && cls.state == ca_connected)
{
byte *data = net_message.data;
int cursize = net_message.cursize;
int i;
for (i = 0 ; i < 2 ; i++)
{
net_message.data = demo_head[i];
net_message.cursize = demo_head_size[i];
CL_WriteDemoMessage();
}
net_message.data = demo_head[2];
SZ_Clear (&net_message);
// current names, colors, and frag counts
for (i=0 ; i < cl.maxclients ; i++)
{
MSG_WriteByte (&net_message, svc_updatename);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl.scores[i].name);
MSG_WriteByte (&net_message, svc_updatefrags);
MSG_WriteByte (&net_message, i);
MSG_WriteShort (&net_message, cl.scores[i].frags);
MSG_WriteByte (&net_message, svc_updatecolors);
MSG_WriteByte (&net_message, i);
MSG_WriteByte (&net_message, cl.scores[i].colors);
}
// send all current light styles
for (i = 0 ; i < MAX_LIGHTSTYLES ; i++)
{
MSG_WriteByte (&net_message, svc_lightstyle);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl_lightstyle[i].map);
}
CL_UpdateDemoStat (STAT_TOTALSECRETS);
CL_UpdateDemoStat (STAT_TOTALMONSTERS);
CL_UpdateDemoStat (STAT_SECRETS);
CL_UpdateDemoStat (STAT_MONSTERS);
for (p=active_particles ; p ; p=p->next) //qbism - send particle snapshot
{
pnext = p->next;
if (pnext)
{
p->next = pnext->next;
pnext->next = free_particles;
free_particles = pnext;
}
CL_UpdateDemoParticle(p);
//qbism - break into several messages if many particles exist
if ((net_message.cursize + sizeof(particle_t)+2000) > net_message.maxsize)
{
CL_WriteDemoMessage();
net_message.data = demo_head[2];
SZ_Clear (&net_message);
}
}
// view entity
MSG_WriteByte (&net_message, svc_setview);
MSG_WriteShort (&net_message, cl.viewentity);
// signon
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 3);
CL_WriteDemoMessage();
// restore net_message
net_message.data = data;
net_message.cursize = cursize;
}
}
In CL_ParseServerMessage, added
Code: Select all
case svc_particledemo: //qbism - snapshot for demos
R_ParseParticleDemo ();
break;
Finally, R_ParseParticleDemo added to r_part.c
Code: Select all
/*
===============
R_ParseParticleDemo
qbism - Parse an individual particle snapshot, made for 'any time' demo recording
===============
*/
void R_ParseParticleDemo (void)
{
int i;
particle_t *p;
if (!free_particles)
Sys_Error("Demo error: free_particles = NULL");
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
for (i=0 ; i<3 ; i++)
p->org[i] = MSG_ReadCoord ();
for (i=0 ; i<3 ; i++)
p->vel[i] = MSG_ReadCoord ();
p->color = MSG_ReadFloat();
p->type = (int)MSG_ReadByte();
p->ramp = MSG_ReadFloat();
p->die = MSG_ReadFloat() + cl.time; //qbism - add back cl.time when parsing
p->start_time = cl.time - MSG_ReadFloat();
p->alpha = MSG_ReadFloat();
p->alphavel = MSG_ReadFloat();
}
Besides visual continuity for players, this would allow more concise demo slices for piecing together videos.