Implement an area grid, primarily to avoid mods(read: xonotic) generating 2000 ents all sitting on the root area node.

Add separate cl_movement cvar to enable/disable reporting input sequences to DP servers (which use different pathways). Does not affect other protocols. This is separate from cl_nopred but will usually have the same result in the long run.
Fixed movevalues for DPP7 clients, if they try using prediction they should now (mostly) get the same values that DP normally uses for QW servers.
Reworked sky overrides somewhat. Now uses skyboxes where possible.
Fixed dpcompat_makeshitup a little, for better compat.
Fixed echo $foo$bar not exanding bar.
Try to fix the meanings of vid_hardwaregamma.
Fixes for builtins/features/etc that apparently only xonotic uses.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5143 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2017-08-29 02:29:06 +00:00
parent 7c6eb18a76
commit 0f7bbfcf0e
90 changed files with 5704 additions and 2907 deletions

View file

@ -1519,9 +1519,12 @@ void CL_Record_f (void)
return; return;
} }
#ifdef Q2SERVER
if (cls.protocol == CP_QUAKE2) if (cls.protocol == CP_QUAKE2)
defaultext = ".dm2"; defaultext = ".dm2";
else if (cls.protocol == CP_NETQUAKE && !CPNQ_IS_DP) else
#endif
if (cls.protocol == CP_NETQUAKE && !CPNQ_IS_DP)
defaultext = ".dem"; defaultext = ".dem";
else if (cls.protocol == CP_QUAKEWORLD) else if (cls.protocol == CP_QUAKEWORLD)
defaultext = ".qwd"; defaultext = ".qwd";
@ -1894,7 +1897,7 @@ void CL_Record_f (void)
// done // done
break; break;
#ifdef Q2CLIENT #if defined(Q2CLIENT) && defined(Q2SERVER)
case CP_QUAKE2: case CP_QUAKE2:
cls.demorecording = DPB_QUAKE2; cls.demorecording = DPB_QUAKE2;

View file

@ -5416,7 +5416,7 @@ void CL_SetSolidEntities (void)
pmove.physents[0].info = 0; pmove.physents[0].info = 0;
pmove.numphysent = 1; pmove.numphysent = 1;
frame = &cl.inframes[parsecountmod]; frame = &cl.inframes[cl.validsequence&UPDATE_MASK];
pak = &frame->packet_entities; pak = &frame->packet_entities;
for (i=0 ; i<pak->num_entities ; i++) for (i=0 ; i<pak->num_entities ; i++)

View file

@ -28,6 +28,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
float in_sensitivityscale = 1; float in_sensitivityscale = 1;
static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue); static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue);
#ifdef NQPROT
cvar_t cl_movement = CVARD("cl_movement","1", "Specifies whether to send movement sequence info over DPP7 protocols (other protocols are unaffected). Unlike cl_nopred, this can result in different serverside behaviour.");
#endif
cvar_t cl_nodelta = CVAR("cl_nodelta","0"); cvar_t cl_nodelta = CVAR("cl_nodelta","0");
@ -48,8 +51,7 @@ cvar_t in_xflip = {"in_xflip", "0"};
cvar_t prox_inmenu = CVAR("prox_inmenu", "0"); cvar_t prox_inmenu = CVAR("prox_inmenu", "0");
usercmd_t independantphysics[MAX_SPLITS]; usercmd_t cl_pendingcmd[MAX_SPLITS];
vec3_t mousemovements[MAX_SPLITS];
/*kinda a hack...*/ /*kinda a hack...*/
unsigned int con_splitmodifier; unsigned int con_splitmodifier;
@ -659,37 +661,31 @@ CL_BaseMove
Send the intended movement message to the server Send the intended movement message to the server
================ ================
*/ */
void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps) void CL_BaseMove (usercmd_t *cmd, int pnum, float priortime, float extratime)
{ {
float scale = 1;//extra/1000.0f * 1/wantfps; float nscale = extratime?extratime / (extratime+priortime):0;
float oscale = 1 - nscale;
// //
// adjust for speed key // adjust for speed key
// //
if ((in_speed.state[pnum] & 1) ^ cl_run.ival) if ((in_speed.state[pnum] & 1) ^ cl_run.ival)
scale *= cl_movespeedkey.value; nscale *= cl_movespeedkey.value;
if (in_strafe.state[pnum] & 1) if (in_strafe.state[pnum] & 1)
{ cmd->sidemove = cmd->sidemove*oscale + nscale*cl_sidespeed.value * (CL_KeyState (&in_right, pnum, true) - CL_KeyState (&in_left, pnum, true)) * (in_xflip.ival?-1:1);
cmd->sidemove += scale*cl_sidespeed.value * CL_KeyState (&in_right, pnum, true); cmd->sidemove = cmd->sidemove*oscale + nscale*cl_sidespeed.value * (CL_KeyState (&in_moveright, pnum, true) - CL_KeyState (&in_moveleft, pnum, true)) * (in_xflip.ival?-1:1);
cmd->sidemove -= scale*cl_sidespeed.value * CL_KeyState (&in_left, pnum, true);
}
cmd->sidemove += scale*cl_sidespeed.value * CL_KeyState (&in_moveright, pnum, true); cmd->upmove = cmd->upmove*oscale + nscale*cl_upspeed.value * (CL_KeyState (&in_up, pnum, true) - CL_KeyState (&in_down, pnum, true));
cmd->sidemove -= scale*cl_sidespeed.value * CL_KeyState (&in_moveleft, pnum, true);
if(in_xflip.ival) cmd->sidemove *= -1;
cmd->upmove += scale*cl_upspeed.value * CL_KeyState (&in_up, pnum, true);
cmd->upmove -= scale*cl_upspeed.value * CL_KeyState (&in_down, pnum, true);
if (! (in_klook.state[pnum] & 1) ) if (! (in_klook.state[pnum] & 1) )
{ {
cmd->forwardmove += scale*cl_forwardspeed.value * CL_KeyState (&in_forward, pnum, true); cmd->forwardmove = cmd->forwardmove*oscale + nscale*(cl_forwardspeed.value * CL_KeyState (&in_forward, pnum, true)) -
cmd->forwardmove -= scale*(*cl_backspeed.string?cl_backspeed.value:cl_forwardspeed.value) * CL_KeyState (&in_back, pnum, true); ((*cl_backspeed.string?cl_backspeed.value:cl_forwardspeed.value) * CL_KeyState (&in_back, pnum, true));
} }
CL_GatherButtons(cmd, pnum); if (!priortime) //only gather buttons if we've not had any this frame. this avoids jump feeling weird with prediction. FIXME: should probably still allow +attack to reduce latency
CL_GatherButtons(cmd, pnum);
} }
void CL_ClampPitch (int pnum) void CL_ClampPitch (int pnum)
@ -902,7 +898,7 @@ void CL_ClampPitch (int pnum)
CL_FinishMove CL_FinishMove
============== ==============
*/ */
void CL_FinishMove (usercmd_t *cmd, int msecs, int pnum) static void CL_FinishMove (usercmd_t *cmd, int pnum)
{ {
int i; int i;
@ -923,9 +919,6 @@ void CL_FinishMove (usercmd_t *cmd, int msecs, int pnum)
CL_GatherButtons(cmd, pnum); CL_GatherButtons(cmd, pnum);
// send milliseconds of time to apply the move
cmd->msec = msecs;
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
cmd->angles[i] = ((int)(cl.playerview[pnum].viewangles[i]*65536.0/360)&65535); cmd->angles[i] = ((int)(cl.playerview[pnum].viewangles[i]*65536.0/360)&65535);
@ -1017,7 +1010,6 @@ void CL_UpdatePrydonCursor(usercmd_t *from, int pnum)
#ifdef NQPROT #ifdef NQPROT
void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf) void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
{ {
static float oldgametime;
int i; int i;
if (cls.demoplayback!=DPB_NONE) if (cls.demoplayback!=DPB_NONE)
@ -1036,8 +1028,7 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
if (cls.protocol_nq >= CPNQ_DP7) if (cls.protocol_nq >= CPNQ_DP7)
{ {
extern cvar_t cl_nopred; if (!cl_movement.ival)
if (cl_nopred.ival)
MSG_WriteLong(buf, 0); MSG_WriteLong(buf, 0);
else else
MSG_WriteLong(buf, cl.movesequence); MSG_WriteLong(buf, cl.movesequence);
@ -1045,9 +1036,7 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
else if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) else if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
MSG_WriteShort(buf, cl.movesequence&0xffff); MSG_WriteShort(buf, cl.movesequence&0xffff);
MSG_WriteFloat (buf, cl.gametime); // so server can get ping times MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports!
cmd->msec = bound(0, cl.gametime - oldgametime, .25)*1000;
oldgametime = cl.gametime;
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
{ {
@ -1108,19 +1097,19 @@ void CLNQ_SendCmd(sizebuf_t *buf)
cl.outframes[i].latency = -1; cl.outframes[i].latency = -1;
cl.outframes[i].server_message_num = cl.validsequence; cl.outframes[i].server_message_num = cl.validsequence;
cl.outframes[i].cmd_sequence = cl.movesequence; cl.outframes[i].cmd_sequence = cl.movesequence;
cl.outframes[i].sentgametime = cl.movesequence_time;
for (seat = 0; seat < cl.splitclients; seat++) for (seat = 0; seat < cl.splitclients; seat++)
{ {
cmd = &cl.outframes[i].cmd[seat]; cmd = &cl.outframes[i].cmd[seat];
*cmd = independantphysics[seat]; *cmd = cl_pendingcmd[seat];
cmd->lightlevel = 0; cmd->fservertime = cl.movesequence_time;
// cmd->msec = (cl.time - cl.outframes[(i-1)&UPDATE_MASK].sentgametime)*1000;
#ifdef CSQC_DAT #ifdef CSQC_DAT
CSQC_Input_Frame(seat, cmd); CSQC_Input_Frame(seat, cmd);
#endif #endif
memset(&independantphysics[seat], 0, sizeof(independantphysics[seat]));
} }
//inputs are only sent once we receive an entity. //inputs are only sent once we receive an entity.
if (cls.signon == 4) if (cls.signon == 4)
{ {
@ -1148,7 +1137,7 @@ void Name_Callback(struct cvar_s *var, char *oldvalue)
} }
#endif #endif
float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now returns the extra time not taken in this slot. Note that negative 1 means uncapped. float CL_FilterTime (double time, float wantfps, float limit, qboolean ignoreserver) //now returns the extra time not taken in this slot. Note that negative 1 means uncapped.
{ {
float fps, fpscap; float fps, fpscap;
@ -1184,8 +1173,8 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now re
return 0; return 0;
//clamp it if we have over 1.5 frame banked somehow //clamp it if we have over 1.5 frame banked somehow
if (time - (1000 / fps) > (1000 / fps)*1.5) if (limit && time - (1000 / fps) > (1000 / fps)*limit)
return (1000 / fps) * 1.5; return (1000 / fps) * limit;
//report how much spare time the caller now has //report how much spare time the caller now has
return time - (1000 / fps); return time - (1000 / fps);
@ -1325,7 +1314,7 @@ int CL_IndepPhysicsThread(void *param)
while(runningindepphys) while(runningindepphys)
{ {
time = Sys_DoubleTime(); time = Sys_DoubleTime();
spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, false); spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, 1.5, false);
if (spare) if (spare)
{ {
time -= spare/1000.0f; time -= spare/1000.0f;
@ -1539,13 +1528,13 @@ qboolean CLQ2_SendCmd (sizebuf_t *buf)
i = cls.netchan.outgoing_sequence & UPDATE_MASK; i = cls.netchan.outgoing_sequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[seat]; cmd = &cl.outframes[i].cmd[seat];
*cmd = independantphysics[seat]; *cmd = cl_pendingcmd[seat];
cmd->lightlevel = (lightlev>255)?255:lightlev; cmd->lightlevel = (lightlev>255)?255:lightlev;
cl.outframes[i].senttime = realtime; cl.outframes[i].senttime = realtime;
cl.outframes[i].latency = -1; cl.outframes[i].latency = -1;
memset(&independantphysics[seat], 0, sizeof(independantphysics[0])); memset(&cl_pendingcmd[seat], 0, sizeof(cl_pendingcmd[seat]));
if (cmd->buttons) if (cmd->buttons)
cmd->buttons |= 128; //fixme: this isn't really what's meant by the anykey. cmd->buttons |= 128; //fixme: this isn't really what's meant by the anykey.
@ -1613,14 +1602,14 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)
} }
cmd = &cl.outframes[curframe].cmd[plnum]; cmd = &cl.outframes[curframe].cmd[plnum];
*cmd = independantphysics[plnum]; *cmd = cl_pendingcmd[plnum];
cmd->lightlevel = 0; cmd->lightlevel = 0;
#ifdef CSQC_DAT #ifdef CSQC_DAT
if (!runningindepphys) if (!runningindepphys)
CSQC_Input_Frame(plnum, cmd); CSQC_Input_Frame(plnum, cmd);
#endif #endif
memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum])); memset(&cl_pendingcmd[plnum], 0, sizeof(cl_pendingcmd[plnum]));
} }
cmd = &cl.outframes[curframe].cmd[0]; cmd = &cl.outframes[curframe].cmd[0];
@ -1706,9 +1695,10 @@ void CL_SendCmd (double frametime, qboolean mainloop)
static int dropcount = 0; static int dropcount = 0;
static double msecs; static double msecs;
static double msecsround; static double msecsround;
int msecstouse;
qboolean dontdrop=false; qboolean dontdrop=false;
float usetime; float usetime; //how many msecs we can use for the new frame
int msecstouse; //usetime truncated to network precision (how much we'll actually eat)
float framemsecs; //how long we're saying the input frame should be (differs from realtime with nq as we want to send frames reguarly, but note this might end up with funny-duration frames).
clcmdbuf_t *next; clcmdbuf_t *next;
@ -1745,6 +1735,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
} }
for (plnum = 0; plnum < cl.splitclients; plnum++) for (plnum = 0; plnum < cl.splitclients; plnum++)
{ {
vec3_t mousemovements;
playerview_t *pv = &cl.playerview[plnum]; playerview_t *pv = &cl.playerview[plnum];
cmd = &cl.outframes[i].cmd[plnum]; cmd = &cl.outframes[i].cmd[plnum];
@ -1754,24 +1745,24 @@ void CL_SendCmd (double frametime, qboolean mainloop)
msecs = 50; msecs = 50;
cmd->msec = msecs; cmd->msec = msecs;
msecs -= cmd->msec; msecs -= cmd->msec;
independantphysics[0].msec = 0; cl_pendingcmd[plnum].msec = 0;
CL_AdjustAngles (plnum, frametime); CL_AdjustAngles (plnum, frametime);
// get basic movement from keyboard // get basic movement from keyboard
CL_BaseMove (cmd, plnum, 1, 1); CL_BaseMove (cmd, plnum, 0, 1);
// allow mice or other external controllers to add to the move // allow mice or other external controllers to add to the move
IN_Move (mousemovements[plnum], plnum, frametime); VectorClear(mousemovements);
independantphysics[plnum].forwardmove += mousemovements[plnum][0]; IN_Move (mousemovements, plnum, frametime);
independantphysics[plnum].sidemove += mousemovements[plnum][1]; cl_pendingcmd[plnum].forwardmove += mousemovements[0];
independantphysics[plnum].upmove += mousemovements[plnum][2]; cl_pendingcmd[plnum].sidemove += mousemovements[1];
VectorClear(mousemovements[plnum]); cl_pendingcmd[plnum].upmove += mousemovements[2];
// if we are spectator, try autocam // if we are spectator, try autocam
if (pv->spectator) if (pv->spectator)
Cam_Track(pv, cmd); Cam_Track(pv, cmd);
CL_FinishMove(cmd, cmd->msec, plnum); CL_FinishMove(cmd, plnum);
Cam_FinishMove(pv, cmd); Cam_FinishMove(pv, cmd);
@ -1812,109 +1803,136 @@ void CL_SendCmd (double frametime, qboolean mainloop)
buf.data = data; buf.data = data;
buf.prim = cls.netchan.message.prim; buf.prim = cls.netchan.message.prim;
#ifdef IRCCONNECT if (cls.protocol == CP_NETQUAKE && cl.time && !cl.paused)
if (cls.netchan.remote_address.type != NA_IRC)
#endif
msecs += frametime*1000;
// Con_Printf("%f\n", msecs);
wantfps = cl_netfps.value;
fullsend = true;
msecstouse = 0;
#ifndef CLIENTONLY
if (sv.state && cls.state != ca_active)
{ {
fullsend = -1; if (cl.movesequence_time > cl.time + 0.5)
msecstouse = usetime = msecs; cl.movesequence_time = cl.time + 0.5; //shouldn't really happen
msecs = 0; if (cl.movesequence_time < cl.time - 0.5)
} cl.movesequence_time = cl.time - 0.5; //shouldn't really happen
else framemsecs = (cl.time - cl.movesequence_time)*1000;
#endif
{ wantfps = cl_netfps.value;
// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up usetime = CL_FilterTime(framemsecs, wantfps, 1.5, false);
if (cls.state < ca_active && !cls.download) if (usetime > 0)
{ {
#ifdef IRCCONNECT //don't spam irc. usetime = framemsecs - usetime;
if (cls.netchan.remote_address.type == NA_IRC) fullsend = true;
wantfps = 0.5;
else
#endif
wantfps = 12.5;
}
if (!runningindepphys && (cl_netfps.value > 0 || !fullsend))
{
float spare;
spare = CL_FilterTime(msecs, wantfps, false);
usetime = msecsround + (msecs - spare);
msecstouse = (int)usetime;
if (!spare)
fullsend = false;
else
{
msecsround = usetime - msecstouse;
msecs = spare + msecstouse;
}
} }
else else
{ {
usetime = msecsround + msecs; usetime = framemsecs - usetime;
msecstouse = (int)usetime; fullsend = false;
msecsround = usetime - msecstouse;
} }
framemsecs = usetime;
} }
else
{
msecs += frametime*1000;
if (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times // Con_Printf("%f\n", msecs);
msecstouse = 200;
// align msecstouse to avoid servers wasting our msecs wantfps = cl_netfps.value;
if (msecstouse > 100) fullsend = true;
msecstouse &= ~3; // align to 4
else if (msecstouse > 50)
msecstouse &= ~1; // align to 2
if (msecstouse < 0) //FIXME msecstouse = 0;
fullsend = false;
if (usetime <= 0) #ifndef CLIENTONLY
return; //infinite frame times = weirdness. if (sv.state && cls.state != ca_active)
{
fullsend = -1;
msecstouse = usetime = msecs;
msecs = 0;
}
else
#endif
{
// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up
if (cls.state < ca_active && !cls.download)
{
#ifdef IRCCONNECT //don't spam irc.
if (cls.netchan.remote_address.type == NA_IRC)
wantfps = 0.5;
else
#endif
wantfps = 12.5;
}
if (!runningindepphys && (cl_netfps.value > 0 || !fullsend))
{
float spare;
spare = CL_FilterTime(msecs, wantfps, (cls.protocol == CP_NETQUAKE?0:1.5), false);
usetime = msecsround + (msecs - spare);
msecstouse = (int)usetime;
if (!spare)
fullsend = false;
else
{
msecsround = usetime - msecstouse;
msecs = spare + msecstouse;
}
}
else
{
usetime = msecsround + msecs;
msecstouse = (int)usetime;
msecsround = usetime - msecstouse;
}
}
if (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times
msecstouse = 200;
// align msecstouse to avoid servers wasting our msecs
if (msecstouse > 100)
msecstouse &= ~3; // align to 4
else if (msecstouse > 50)
msecstouse &= ~1; // align to 2
if (msecstouse <= 0) //FIXME
fullsend = false;
if (usetime <= 0)
return; //infinite frame times = weirdness.
cl.movesequence_time = cl.time;
framemsecs = msecstouse;
if (cls.protocol == CP_NETQUAKE)
framemsecs = 0;
}
#ifdef HLCLIENT #ifdef HLCLIENT
if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0])) if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0]))
#endif #endif
for (plnum = 0; plnum < cl.splitclients; plnum++) for (plnum = 0; plnum < cl.splitclients; plnum++)
{ {
// CL_BaseMove (&independantphysics[plnum], plnum, (msecstouse - independantphysics[plnum].msec), wantfps); vec3_t mousemovements;
CL_AdjustAngles (plnum, frametime); CL_AdjustAngles (plnum, frametime);
IN_Move (mousemovements[plnum], plnum, frametime); VectorClear(mousemovements);
IN_Move (mousemovements, plnum, frametime);
CL_ClampPitch(plnum); CL_ClampPitch(plnum);
independantphysics[plnum].forwardmove += mousemovements[plnum][0]; cl_pendingcmd[plnum].forwardmove += mousemovements[0]; //FIXME: this will get nuked by CL_BaseMove.
independantphysics[plnum].sidemove += mousemovements[plnum][1]; cl_pendingcmd[plnum].sidemove += mousemovements[1];
independantphysics[plnum].upmove += mousemovements[plnum][2]; cl_pendingcmd[plnum].upmove += mousemovements[2];
VectorClear(mousemovements[plnum]);
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
independantphysics[plnum].angles[i] = ((int)(cl.playerview[plnum].viewangles[i]*65536.0/360)&65535); cl_pendingcmd[plnum].angles[i] = ((int)(cl.playerview[plnum].viewangles[i]*65536.0/360)&65535);
if (!independantphysics[plnum].msec) CL_BaseMove (&cl_pendingcmd[plnum], plnum, cl_pendingcmd[plnum].msec, framemsecs);
if (!cl_pendingcmd[plnum].msec)
{ {
CL_BaseMove (&independantphysics[plnum], plnum, (msecstouse - independantphysics[plnum].msec), wantfps); CL_FinishMove(&cl_pendingcmd[plnum], plnum);
CL_FinishMove(&independantphysics[plnum], msecstouse, plnum);
Cbuf_Waited(); //its okay to stop waiting now Cbuf_Waited(); //its okay to stop waiting now
} }
cl_pendingcmd[plnum].msec = framemsecs;
// if we are spectator, try autocam // if we are spectator, try autocam
// if (cl.spectator) // if (cl.spectator)
Cam_Track(&cl.playerview[plnum], &independantphysics[plnum]); Cam_Track(&cl.playerview[plnum], &cl_pendingcmd[plnum]);
Cam_FinishMove(&cl.playerview[plnum], &independantphysics[plnum]); Cam_FinishMove(&cl.playerview[plnum], &cl_pendingcmd[plnum]);
independantphysics[plnum].msec = usetime;
//HACK: 1000/77 = 12.98. nudge it just under so we never appear to be using 83fps at 77fps (which can trip cheat detection in mods that expect 72 fps when many servers are configured for 77) //HACK: 1000/77 = 12.98. nudge it just under so we never appear to be using 83fps at 77fps (which can trip cheat detection in mods that expect 72 fps when many servers are configured for 77)
//so lets just never use 12. //so lets just never use 12.
if (fullsend && (independantphysics[plnum].msec > 12.9 && independantphysics[plnum].msec < 13) && cls.maxfps == 77) if (fullsend && (cl_pendingcmd[plnum].msec > 12.9 && cl_pendingcmd[plnum].msec < 13) && cls.maxfps == 77)
independantphysics[plnum].msec = 13; cl_pendingcmd[plnum].msec = 13;
} }
//the main loop isn't allowed to send //the main loop isn't allowed to send
@ -1977,24 +1995,29 @@ void CL_SendCmd (double frametime, qboolean mainloop)
} }
cursor_active = false; cursor_active = false;
cmd = &independantphysics[0]; for (plnum = 0; plnum < cl.splitclients; plnum++)
if (((cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)||(cls.protocol == CP_NETQUAKE && cls.protocol_nq >= CPNQ_DP6)) &&
(*cl_prydoncursor.string && cl_prydoncursor.ival >= 0) && cls.state == ca_active)
CL_UpdatePrydonCursor(cmd, 0);
else
{ {
Vector2Clear(cmd->cursor_screen); cmd = &cl_pendingcmd[plnum];
VectorClear(cmd->cursor_start); if (((cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)||(cls.protocol == CP_NETQUAKE && cls.protocol_nq >= CPNQ_DP6)) &&
VectorClear(cmd->cursor_impact); (*cl_prydoncursor.string && cl_prydoncursor.ival >= 0) && cls.state == ca_active)
cmd->cursor_entitynumber = 0; CL_UpdatePrydonCursor(cmd, plnum);
else
{
Vector2Clear(cmd->cursor_screen);
VectorClear(cmd->cursor_start);
VectorClear(cmd->cursor_impact);
cmd->cursor_entitynumber = 0;
}
} }
cl.movesequence_time += framemsecs/1000.0;
switch (cls.protocol) switch (cls.protocol)
{ {
#ifdef NQPROT #ifdef NQPROT
case CP_NETQUAKE: case CP_NETQUAKE:
msecs -= (double)msecstouse; msecs -= (double)msecstouse;
CLNQ_SendCmd (&buf); CLNQ_SendCmd (&buf);
dontdrop = true;
break; break;
#endif #endif
case CP_QUAKEWORLD: case CP_QUAKEWORLD:
@ -2010,8 +2033,8 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#ifdef Q3CLIENT #ifdef Q3CLIENT
case CP_QUAKE3: case CP_QUAKE3:
msecs -= (double)msecstouse; msecs -= (double)msecstouse;
CLQ3_SendCmd(&independantphysics[0]); CLQ3_SendCmd(&cl_pendingcmd[0]);
memset(&independantphysics[0], 0, sizeof(independantphysics[0])); memset(&cl_pendingcmd[0], 0, sizeof(cl_pendingcmd[0]));
//don't bank too much, because that results in banking speedcheats //don't bank too much, because that results in banking speedcheats
if (msecs > 200) if (msecs > 200)
@ -2022,17 +2045,22 @@ void CL_SendCmd (double frametime, qboolean mainloop)
Host_EndGame("Invalid protocol in CL_SendCmd: %i", cls.protocol); Host_EndGame("Invalid protocol in CL_SendCmd: %i", cls.protocol);
return; return;
} }
if (cls.demorecording)
CL_WriteDemoCmd(&cl.outframes[cl.movesequence & UPDATE_MASK].cmd[0]);
// Con_DPrintf("generated sequence %i\n", cl.movesequence);
cl.movesequence++;
//clear enough of the pending command for the next frame.
for (plnum = 0; plnum < cl.splitclients; plnum++)
{
cl_pendingcmd[plnum].msec = 0;
cl_pendingcmd[plnum].impulse = 0;
cl_pendingcmd[plnum].buttons = 0;
}
} }
i = cl.movesequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[0];
if (cls.demorecording)
CL_WriteDemoCmd(cmd);
// Con_DPrintf("generated sequence %i\n", cl.movesequence);
cl.movesequence++;
#ifdef IRCCONNECT #ifdef IRCCONNECT
if (cls.netchan.remote_address.type == NA_IRC) if (cls.netchan.remote_address.type == NA_IRC)
{ {
@ -2043,7 +2071,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
else else
{ {
// don't count this message when calculating PL // don't count this message when calculating PL
cl.outframes[i].latency = -3; cl.outframes[cls.netchan.outgoing_sequence&UPDATE_MASK].latency = -3;
// drop this message // drop this message
cls.netchan.outgoing_sequence++; cls.netchan.outgoing_sequence++;
dropcount++; dropcount++;
@ -2157,6 +2185,10 @@ void CL_InitInput (void)
Cvar_Register (&cl_sparemsec, inputnetworkcvargroup); Cvar_Register (&cl_sparemsec, inputnetworkcvargroup);
Cvar_Register (&cl_run, inputnetworkcvargroup); Cvar_Register (&cl_run, inputnetworkcvargroup);
#ifdef NQPROT
Cvar_Register (&cl_movement, inputnetworkcvargroup);
#endif
Cvar_Register (&cl_smartjump, inputnetworkcvargroup); Cvar_Register (&cl_smartjump, inputnetworkcvargroup);
Cvar_Register (&cl_prydoncursor, inputnetworkcvargroup); Cvar_Register (&cl_prydoncursor, inputnetworkcvargroup);

View file

@ -101,10 +101,10 @@ cvar_t record_flush = CVARD("record_flush", "0", "If set, explicitly flushes dem
cvar_t cl_demospeed = CVARF("cl_demospeed", "1", 0); cvar_t cl_demospeed = CVARF("cl_demospeed", "1", 0);
cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup."); cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup.");
cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, dp6, dp7. If empty, will use qw protocols for qw mods, and nq protocols for nq mods."); cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods.");
cvar_t cl_threadedphysics = CVAR("cl_threadedphysics", "0"); cvar_t cl_threadedphysics = CVARD("cl_threadedphysics", "0", "When set, client input frames are generated and sent on a worker thread");
#ifdef QUAKESPYAPI #ifdef QUAKESPYAPI
cvar_t localid = SCVAR("localid", ""); cvar_t localid = SCVAR("localid", "");
@ -124,8 +124,8 @@ cvar_t password = CVARAF("password", "", "pq_password", CVAR_USERINFO | CVAR_NOU
cvar_t spectator = CVARF("spectator", "", CVAR_USERINFO); cvar_t spectator = CVARF("spectator", "", CVAR_USERINFO);
cvar_t name = CVARFC("name", "Player", CVAR_ARCHIVE | CVAR_USERINFO, Name_Callback); cvar_t name = CVARFC("name", "Player", CVAR_ARCHIVE | CVAR_USERINFO, Name_Callback);
cvar_t team = CVARF("team", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t team = CVARF("team", "", CVAR_ARCHIVE | CVAR_USERINFO);
cvar_t skin = CVARF("skin", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t skin = CVARAF("skin", "", "_cl_playerskin", CVAR_ARCHIVE | CVAR_USERINFO);
cvar_t model = CVARF("model", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t model = CVARAF("model", "", "_cl_playermodel", CVAR_ARCHIVE | CVAR_USERINFO);
cvar_t topcolor = CVARF("topcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t topcolor = CVARF("topcolor", "", CVAR_ARCHIVE | CVAR_USERINFO);
cvar_t bottomcolor = CVARF("bottomcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t bottomcolor = CVARF("bottomcolor", "", CVAR_ARCHIVE | CVAR_USERINFO);
cvar_t rate = CVARFD("rate", "30000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'."); cvar_t rate = CVARFD("rate", "30000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'.");
@ -422,7 +422,7 @@ void CL_ConnectToDarkPlaces(char *challenge, netadr_t *adr)
connectinfo.time = realtime; // for retransmit requests connectinfo.time = realtime; // for retransmit requests
Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect\\protocol\\darkplaces 3\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP NEHAHRABJP2 NEHAHRABJP3 QUAKE\\challenge\\%s", 255, 255, 255, 255, challenge); Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect\\protocol\\darkplaces 3\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\challenge\\%s\\name\\%s", 255, 255, 255, 255, challenge, name.string);
NET_SendPacket (NS_CLIENT, strlen(data), data, adr); NET_SendPacket (NS_CLIENT, strlen(data), data, adr);
@ -714,6 +714,7 @@ void CL_CheckForResend (void)
qboolean keeptrying = true; qboolean keeptrying = true;
char *host; char *host;
extern int r_blockvidrestart; extern int r_blockvidrestart;
const char *lbp;
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
@ -753,24 +754,28 @@ void CL_CheckForResend (void)
#endif #endif
default: default:
cl.movesequence = 0; cl.movesequence = 0;
if (!strcmp(cl_loopbackprotocol.string, "qw") || progstype == PROG_H2) lbp = cl_loopbackprotocol.string;
if (!strcmp(lbp, "") || !strcmp(lbp, "qw") || progstype == PROG_H2)
{ //qw with all supported extensions -default { //qw with all supported extensions -default
//for hexen2 we always force fte's native qw protocol. other protocols won't cut it.
connectinfo.protocol = CP_QUAKEWORLD;
connectinfo.subprotocol = PROTOCOL_VERSION_QW;
connectinfo.fteext1 = Net_PextMask(1, false); connectinfo.fteext1 = Net_PextMask(1, false);
connectinfo.fteext2 = Net_PextMask(2, false); connectinfo.fteext2 = Net_PextMask(2, false);
connectinfo.protocol = CP_QUAKEWORLD;
connectinfo.subprotocol = PROTOCOL_VERSION_QW;
} }
else if (!strcmp(cl_loopbackprotocol.string, "qwid") || !strcmp(cl_loopbackprotocol.string, "idqw")) else if (!strcmp(lbp, "qwid") || !strcmp(cl_loopbackprotocol.string, "idqw"))
{ { //for recording .qwd files in any client
connectinfo.protocol = CP_QUAKEWORLD; connectinfo.protocol = CP_QUAKEWORLD;
connectinfo.subprotocol = PROTOCOL_VERSION_QW; connectinfo.subprotocol = PROTOCOL_VERSION_QW;
connectinfo.fteext1 = 0;
connectinfo.fteext2 = 0;
} }
#ifdef Q3CLIENT #ifdef Q3CLIENT
else if (!strcmp(cl_loopbackprotocol.string, "q3")) else if (!strcmp(lbp, "q3"))
cls.protocol = CP_QUAKE3; cls.protocol = CP_QUAKE3;
#endif #endif
#ifdef NQPROT #ifdef NQPROT
else if (!strcmp(cl_loopbackprotocol.string, "random")) else if (!strcmp(lbp, "random"))
{ //for debugging. { //for debugging.
if (rand() & 1) if (rand() & 1)
{ {
@ -785,33 +790,33 @@ void CL_CheckForResend (void)
connectinfo.fteext2 = Net_PextMask(2, false); connectinfo.fteext2 = Net_PextMask(2, false);
} }
} }
else if (!strcmp(cl_loopbackprotocol.string, "fitz") || !strcmp(cl_loopbackprotocol.string, "666") || !strcmp(cl_loopbackprotocol.string, "999")) else if (!strcmp(lbp, "fitz") || !strcmp(lbp, "666") || !strcmp(lbp, "999"))
{ { //we don't really distinguish between fitz and rmq protocols. we just use 999 with bigcoords and 666 othewise.
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666; connectinfo.subprotocol = CPNQ_FITZ666;
} }
else if (!strcmp(cl_loopbackprotocol.string, "bjp3") || !strcmp(cl_loopbackprotocol.string, "bjp")) else if (!strcmp(lbp, "bjp3") || !strcmp(lbp, "bjp"))
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_BJP3; connectinfo.subprotocol = CPNQ_BJP3;
} }
else if (!strcmp(cl_loopbackprotocol.string, "nq")) else if (!strcmp(lbp, "nq"))
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_ID; connectinfo.subprotocol = CPNQ_ID;
proquakeangles = true; proquakeangles = true;
} }
else if (!strcmp(cl_loopbackprotocol.string, "nqid") || !strcmp(cl_loopbackprotocol.string, "idnq")) else if (!strcmp(lbp, "nqid") || !strcmp(lbp, "idnq"))
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_ID; connectinfo.subprotocol = CPNQ_ID;
} }
else if (!strcmp(cl_loopbackprotocol.string, "dp6") || !strcmp(cl_loopbackprotocol.string, "dpp6")) else if (!strcmp(lbp, "dp6") || !strcmp(lbp, "dpp6"))
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_DP6; connectinfo.subprotocol = CPNQ_DP6;
} }
else if (!strcmp(cl_loopbackprotocol.string, "dp7") || !strcmp(cl_loopbackprotocol.string, "dpp7")) else if (!strcmp(lbp, "dp7") || !strcmp(lbp, "dpp7"))
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_DP7; connectinfo.subprotocol = CPNQ_DP7;
@ -820,7 +825,8 @@ void CL_CheckForResend (void)
{ {
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666; connectinfo.subprotocol = CPNQ_FITZ666;
//FIXME: pext connectinfo.fteext1 = Net_PextMask(1, true);
connectinfo.fteext2 = Net_PextMask(2, true);
} }
#endif #endif
else else
@ -932,7 +938,7 @@ void CL_CheckForResend (void)
} }
else else
CL_ConnectToDarkPlaces("", &connectinfo.adr); CL_ConnectToDarkPlaces("", &connectinfo.adr);
connectinfo.trying = false; // connectinfo.trying = false;
} }
else else
#endif #endif
@ -1904,6 +1910,9 @@ void CL_Color_f (void)
qboolean server_owns_colour; qboolean server_owns_colour;
char *t;
char *b;
if (Cmd_Argc() == 1) if (Cmd_Argc() == 1)
{ {
char *t = Info_ValueForKey (cls.userinfo[pnum], "topcolor"); char *t = Info_ValueForKey (cls.userinfo[pnum], "topcolor");
@ -1923,13 +1932,14 @@ void CL_Color_f (void)
server_owns_colour = false; server_owns_colour = false;
if (Cmd_Argc() == 2) t = Cmd_Argv(1);
top = bottom = CL_ParseColour(Cmd_Argv(1)); b = (Cmd_Argc()==2)?t:Cmd_Argv(2);
else if (!strcmp(t, "-1"))
{ t = Info_ValueForKey (cls.userinfo[pnum], "topcolor");
top = CL_ParseColour(Cmd_Argv(1)); top = CL_ParseColour(t);
bottom = CL_ParseColour(Cmd_Argv(2)); if (!strcmp(b, "-1"))
} b = Info_ValueForKey (cls.userinfo[pnum], "bottomcolor");
bottom = CL_ParseColour(b);
Q_snprintfz (num, sizeof(num), (top&0xff000000)?"0x%06x":"%i", top & 0xffffff); Q_snprintfz (num, sizeof(num), (top&0xff000000)?"0x%06x":"%i", top & 0xffffff);
if (top == 0) if (top == 0)
@ -5419,7 +5429,7 @@ double Host_Frame (double time)
) )
{ {
// realtime += spare/1000; //don't use it all! // realtime += spare/1000; //don't use it all!
double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver); double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, 1.5, maxfpsignoreserver);
if (!newspare) if (!newspare)
{ {
while(COM_DoWork(0, false)) while(COM_DoWork(0, false))

View file

@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h" #include "quakedef.h"
#include "cl_ignore.h" #include "cl_ignore.h"
#include "shader.h"
void CL_GetNumberedEntityInfo (int num, float *org, float *ang); void CL_GetNumberedEntityInfo (int num, float *org, float *ang);
void CLDP_ParseDarkPlaces5Entities(void); void CLDP_ParseDarkPlaces5Entities(void);
@ -3736,8 +3737,8 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
CL_SendClientCommand(true, "color %i %i\n", topcolor.ival, bottomcolor.ival); CL_SendClientCommand(true, "color %i %i\n", topcolor.ival, bottomcolor.ival);
if (cl.haveserverinfo) if (cl.haveserverinfo)
Info_Enumerate(cls.userinfo[0], NULL, CLNQ_SendInitialUserInfo); Info_Enumerate(cls.userinfo[0], NULL, CLNQ_SendInitialUserInfo);
else if (CPNQ_IS_DP) //dp needs a couple of extras to work properly. don't send them on other servers because that generally results in error messages. else if (CPNQ_IS_DP)
{ { //dp needs a couple of extras to work properly in certain cases. don't send them on other servers because that generally results in error messages.
CL_SendClientCommand(true, "rate %s", rate.string); CL_SendClientCommand(true, "rate %s", rate.string);
CL_SendClientCommand(true, "playermodel %s", model.string); CL_SendClientCommand(true, "playermodel %s", model.string);
CL_SendClientCommand(true, "playerskin %s", skin.string); CL_SendClientCommand(true, "playerskin %s", skin.string);
@ -4170,9 +4171,7 @@ static void CLQ2_ParseConfigString (void)
Q_strncpyz (cl.levelname, s, sizeof(cl.levelname)); Q_strncpyz (cl.levelname, s, sizeof(cl.levelname));
} }
else if (i == Q2CS_SKY) else if (i == Q2CS_SKY)
{ R_SetSky(s);
Q_strncpyz (cl.skyname, s, sizeof(cl.skyname));
}
else if (i == Q2CS_SKYAXIS) else if (i == Q2CS_SKYAXIS)
{ {
s = COM_Parse(s); s = COM_Parse(s);
@ -4819,15 +4818,13 @@ void CL_ParseClientdata (void)
oldparsecountmod = cl.oldparsecount & UPDATE_MASK; oldparsecountmod = cl.oldparsecount & UPDATE_MASK;
} }
cl.parsecount = i; cl.parsecount = i;
i &= UPDATE_MASK; parsecountmod = i&UPDATE_MASK;
parsecountmod = i;
parsecounttime = realtime;//cl.outframes[i].senttime; parsecounttime = realtime;//cl.outframes[i].senttime;
if (cls.protocol == CP_QUAKEWORLD) if (cls.protocol == CP_QUAKEWORLD)
CL_AckedInputFrame(cls.netchan.incoming_sequence, cl.parsecount, false); CL_AckedInputFrame(cls.netchan.incoming_sequence, cl.parsecount, false);
} }
#include "shader.h"
static qboolean CLQ2_PlayerSkinIsOkay(skinid_t id) static qboolean CLQ2_PlayerSkinIsOkay(skinid_t id)
{ {
skinfile_t *sk = Mod_LookupSkin(id); skinfile_t *sk = Mod_LookupSkin(id);
@ -5026,10 +5023,10 @@ static void CL_ProcessUserInfo (int slot, player_info_t *player)
{ {
Cam_Unlock(&cl.playerview[i]); Cam_Unlock(&cl.playerview[i]);
} }
CL_CheckServerInfo();
} }
// Update the rules since spectators can bypass everything but players can't // Update the rules since spectators can bypass everything but players can't
if (ospec != player->spectator) else if (ospec != player->spectator)
CL_CheckServerInfo(); CL_CheckServerInfo();
Skin_FlushPlayers(); Skin_FlushPlayers();
@ -7498,8 +7495,6 @@ void CLNQ_ParseServerMessage (void)
else if (cl_shownet.value == 2) else if (cl_shownet.value == 2)
Con_Printf ("------------------\n"); Con_Printf ("------------------\n");
CL_ParseClientdata ();
// //
// parse the message // parse the message
// //
@ -7648,6 +7643,7 @@ void CLNQ_ParseServerMessage (void)
cls.signon = 4; cls.signon = 4;
CLNQ_SignonReply (); CLNQ_SignonReply ();
} }
CL_ParseClientdata ();
CLFTE_ParseEntities(); CLFTE_ParseEntities();
break; break;
case svcfte_spawnstatic2: case svcfte_spawnstatic2:
@ -7670,6 +7666,8 @@ void CLNQ_ParseServerMessage (void)
break; break;
case svc_time: case svc_time:
CL_ParseClientdata ();
//fixme: move this stuff to a common place //fixme: move this stuff to a common place
// cl.playerview[destsplit].oldfixangle = cl.playerview[destsplit].fixangle; // cl.playerview[destsplit].oldfixangle = cl.playerview[destsplit].fixangle;
// VectorCopy(cl.playerview[destsplit].fixangles, cl.playerview[destsplit].oldfixangles); // VectorCopy(cl.playerview[destsplit].fixangles, cl.playerview[destsplit].oldfixangles);
@ -7933,8 +7931,7 @@ void CLNQ_ParseServerMessage (void)
break; break;
case svcfitz_skybox: case svcfitz_skybox:
Q_strncpyz(cl.skyname, MSG_ReadString(), sizeof(cl.skyname)); R_SetSky(MSG_ReadString());
R_SetSky(cl.skyname);
break; break;
case svcfitz_bf: case svcfitz_bf:
Cmd_ExecuteString("bf", RESTRICT_SERVER); Cmd_ExecuteString("bf", RESTRICT_SERVER);

View file

@ -23,12 +23,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
cvar_t cl_predict_extrapolate = CVARD("cl_predict_extrapolate", "", "If 1, enables prediction based upon partial input frames which can change over time resulting in a swimmy feel but does not need to interpolate. If 0, prediction will stay in the past and thus use only completed frames. Interpolation will then be used to smooth movement.\nThis cvar only applies when video and input frames are independant (ie: cl_netfps is set)."); cvar_t cl_predict_extrapolate = CVARD("cl_predict_extrapolate", "", "If 1, enables prediction based upon partial input frames which can change over time resulting in a swimmy feel but does not need to interpolate. If 0, prediction will stay in the past and thus use only completed frames. Interpolation will then be used to smooth movement.\nThis cvar only applies when video and input frames are independant (ie: cl_netfps is set).");
cvar_t cl_predict_timenudge = CVARD("cl_predict_timenudge", "0", "A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames."); cvar_t cl_predict_timenudge = CVARD("cl_predict_timenudge", "0", "A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames.");
cvar_t cl_predict_smooth = CVARD("cl_lerp_smooth", "2", "If 2, will act as 1 when playing demos/singleplayer and otherwise act as if set to 0 (ie: deathmatch).\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss."); cvar_t cl_predict_smooth = CVARD("cl_lerp_smooth", "2", "If 2, will act as 1 when playing demos/singleplayer and otherwise act as if set to 0 (ie: deathmatch).\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss.");
cvar_t cl_nopred = CVAR("cl_nopred","0"); cvar_t cl_nopred = CVARD("cl_nopred","0", "Disables clientside movement prediction.");
cvar_t cl_pushlatency = CVAR("pushlatency","-999"); cvar_t cl_pushlatency = CVAR("pushlatency","-999");
extern float pm_airaccelerate; extern float pm_airaccelerate;
extern usercmd_t independantphysics[MAX_SPLITS]; extern usercmd_t cl_pendingcmd[MAX_SPLITS];
#ifdef Q2CLIENT #ifdef Q2CLIENT
#define MAX_PARSE_ENTITIES 1024 #define MAX_PARSE_ENTITIES 1024
@ -297,10 +297,10 @@ static void CLQ2_PredictMovement (int seat) //q2 doesn't support split clients.
VectorCopy (pm.s.origin, cl_predicted_origins[seat][frame]); VectorCopy (pm.s.origin, cl_predicted_origins[seat][frame]);
} }
if (independantphysics[seat].msec) if (cl_pendingcmd[seat].msec)
{ {
cmd = (q2usercmd_t*)&independantphysics[seat]; cmd = (q2usercmd_t*)&cl_pendingcmd[seat];
cmd->msec = independantphysics[seat].msec; cmd->msec = cl_pendingcmd[seat].msec;
pm.cmd = *cmd; pm.cmd = *cmd;
Q2_Pmove (&pm); Q2_Pmove (&pm);
@ -891,12 +891,14 @@ void CL_PredictMovePNum (int seat)
// netfps = fps; // netfps = fps;
if (!*cl_predict_extrapolate.string) if (!*cl_predict_extrapolate.string)
extrap = netfps < 30; extrap = netfps < 30;
if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)
extrap = true; //DP servers do a nasty thing where they send packets without any entities. This messes with our timings. Its much smoother to just always use extrapolation in this case (otherwise we'd have to backdate too much for prediction to do much).
if (!extrap) if (!extrap)
{ {
//interpolate. The input rate is completely smoothed out, at the cost of some latency. //interpolate. The input rate is completely smoothed out, at the cost of some latency.
//You can still get juddering if the video rate doesn't match the monitor refresh rate (and isn't so high that it doesn't matter). //You can still get juddering if the video rate doesn't match the monitor refresh rate (and isn't so high that it doesn't matter).
//note that the code below will back-date input frames if the server acks too fast. //note that the code below will back-date input frames if the server acks too fast.
simtime = realtime - (1/netfps); simtime = realtime - (1.0/netfps);
} }
else else
{ {
@ -907,7 +909,7 @@ void CL_PredictMovePNum (int seat)
} }
if (cls.demoplayback == DPB_QUAKEWORLD || pv->cam_state == CAM_EYECAM) if (cls.demoplayback == DPB_QUAKEWORLD || pv->cam_state == CAM_EYECAM)
simtime -= cls.latency; simtime -= cls.latency; //push back when playing demos.
simtime += bound(-0.5, cl_predict_timenudge.value, 0.5); simtime += bound(-0.5, cl_predict_timenudge.value, 0.5);
pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD);
@ -1209,8 +1211,8 @@ void CL_PredictMovePNum (int seat)
cmdfrom = cmdto; cmdfrom = cmdto;
tostate = &framebuf[i++&1]; tostate = &framebuf[i++&1];
if (independantphysics[seat].msec && !cls.demoplayback) if (cl_pendingcmd[seat].msec && !cls.demoplayback)
indcmd = independantphysics[seat]; indcmd = cl_pendingcmd[seat];
else else
indcmd = *cmdto; indcmd = *cmdto;
cmdto = &indcmd; cmdto = &indcmd;

View file

@ -46,28 +46,49 @@ void RSpeedShow(void)
return; return;
memset(RSpNames, 0, sizeof(RSpNames)); memset(RSpNames, 0, sizeof(RSpNames));
RSpNames[RSPEED_TOTALREFRESH] = "Total refresh";
RSpNames[RSPEED_PROTOCOL] = "Protocol";
RSpNames[RSPEED_LINKENTITIES] = "Entity setup";
RSpNames[RSPEED_WORLDNODE] = "World walking";
RSpNames[RSPEED_WORLD] = "World rendering";
RSpNames[RSPEED_DYNAMIC] = "Lightmap updates";
RSpNames[RSPEED_PARTICLES] = "Particle phys/sort";
RSpNames[RSPEED_PARTICLESDRAW] = "Particle drawing";
RSpNames[RSPEED_2D] = "2d elements";
RSpNames[RSPEED_SERVER] = "Server";
RSpNames[RSPEED_DRAWENTITIES] = "Entity rendering"; RSPEED_TOTALREFRESH,
RSPEED_CSQCPHYSICS,
RSPEED_CSQCREDRAW,
RSPEED_LINKENTITIES,
RSPEED_WORLDNODE,
RSPEED_DYNAMIC,
RSPEED_OPAQUE,
RSPEED_RTLIGHTS,
RSPEED_TRANSPARENTS,
RSPEED_PROTOCOL,
RSPEED_PARTICLES,
RSPEED_PARTICLESDRAW,
RSPEED_PALETTEFLASHES,
RSPEED_2D,
RSPEED_SERVER,
RSPEED_SETUP,
RSPEED_SUBMIT,
RSPEED_PRESENT,
RSPEED_ACQUIRE,
RSpNames[RSPEED_PALETTEFLASHES] = "Palette flashes";
RSpNames[RSPEED_STENCILSHADOWS] = "Stencil Shadows";
RSpNames[RSPEED_FULLBRIGHTS] = "World fullbrights"; RSpNames[RSPEED_TOTALREFRESH] = "Total refresh";
RSpNames[RSPEED_SETUP] = "Setup/Acquire"; RSpNames[RSPEED_CSQCPHYSICS] = " CSQC Physics";
RSpNames[RSPEED_CSQCREDRAW] = " CSQC Drawing";
RSpNames[RSPEED_LINKENTITIES] = " Entity setup";
RSpNames[RSPEED_WORLDNODE] = " World walking";
RSpNames[RSPEED_DYNAMIC] = " Lightmap updates";
RSpNames[RSPEED_OPAQUE] = " Opaque Batches";
RSpNames[RSPEED_RTLIGHTS] = " RT Lights";
RSpNames[RSPEED_TRANSPARENTS] = " Transparent Batches";
RSpNames[RSPEED_PARTICLES] = " Particle phys/sort";
RSpNames[RSPEED_PARTICLESDRAW] = " Particle drawing";
RSpNames[RSPEED_2D] = " 2d Elements";
RSpNames[RSPEED_PALETTEFLASHES] = " Palette flashes";
RSpNames[RSPEED_SUBMIT] = "submit/finish"; RSpNames[RSPEED_SETUP] = "Acquire Wait";
RSpNames[RSPEED_PRESENT] = "present"; RSpNames[RSPEED_SUBMIT] = "submit/finish";
RSpNames[RSPEED_ACQUIRE] = "acquire"; RSpNames[RSPEED_PRESENT] = "Present";
RSpNames[RSPEED_ACQUIRE] = "Acquire Request";
RSpNames[RSPEED_PROTOCOL] = "Client Protocol";
RSpNames[RSPEED_SERVER] = "Server";
memset(RQntNames, 0, sizeof(RQntNames)); memset(RQntNames, 0, sizeof(RQntNames));
RQntNames[RQUANT_MSECS] = "Microseconds"; RQntNames[RQUANT_MSECS] = "Microseconds";
@ -91,8 +112,9 @@ void RSpeedShow(void)
{ {
for (i = 0; i < RSPEED_MAX; i++) for (i = 0; i < RSPEED_MAX; i++)
{ {
s = va("%g %-20s", samplerspeeds[i]/(float)frameinterval, RSpNames[i]); Draw_FunStringWidth(vid.width-20*8, i*8, RSpNames[i], 20*8, false, false);
Draw_FunString(vid.width-strlen(s)*8, i*8, s); s = va("%g ", samplerspeeds[i]/(float)frameinterval);
Draw_FunStringWidth(0, i*8, s, vid.width-20*8, true, false);
} }
} }
for (i = 0; i < RQUANT_MAX; i++) for (i = 0; i < RQUANT_MAX; i++)
@ -102,7 +124,7 @@ void RSpeedShow(void)
} }
if (r_speeds.ival > 1) if (r_speeds.ival > 1)
{ {
s = va("%f %-20s", (frameinterval*1000*1000.0f)/(samplerspeeds[RSPEED_TOTALREFRESH]+samplerspeeds[RSPEED_PRESENT]), "Framerate (refresh only)"); s = va("%f %-20s", (frameinterval*1000*1000.0f)/samplerspeeds[RSPEED_TOTALREFRESH], "Framerate (refresh only)");
Draw_FunString(vid.width-strlen(s)*8, (i+RSPEED_MAX)*8, s); Draw_FunString(vid.width-strlen(s)*8, (i+RSPEED_MAX)*8, s);
} }
memcpy(rquant, savedsamplerquant, sizeof(rquant)); memcpy(rquant, savedsamplerquant, sizeof(rquant));
@ -224,7 +246,7 @@ float scr_disabled_time;
float oldsbar = 0; float oldsbar = 0;
cvar_t con_stayhidden = CVARFD("con_stayhidden", "0", CVAR_NOTFROMSERVER, "0: allow console to pounce on the user\n1: console stays hidden unless explicitly invoked\n2:toggleconsole command no longer works\n3: shift+escape key no longer works"); cvar_t con_stayhidden = CVARFD("con_stayhidden", "0", CVAR_NOTFROMSERVER, "0: allow console to pounce on the user\n1: console stays hidden unless explicitly invoked\n2:toggleconsole command no longer works\n3: shift+escape key no longer works");
cvar_t show_fps = CVARF("show_fps", "0", CVAR_ARCHIVE); cvar_t show_fps = CVARFD("show_fps", "0", CVAR_ARCHIVE, "Displays the current framerate on-screen.\n1: framerate average over a second.\n2: Slowest frame over the last second (the game will play like shit if this is significantly lower than the average).\n3: Shows the rate of the fastest frame (not very useful).\n4: Shows the current frame's timings (this depends upon timer precision).\n5: Display a graph of how long it took to render each frame, large spikes are BAD BAD BAD.\n6: Displays the standard deviation of the frame times, if its greater than 3 then something is probably badly made, or you've a virus scanner running...\n7: Framegraph, for use with slower frames.");
cvar_t show_fps_x = CVAR("show_fps_x", "-1"); cvar_t show_fps_x = CVAR("show_fps_x", "-1");
cvar_t show_fps_y = CVAR("show_fps_y", "-1"); cvar_t show_fps_y = CVAR("show_fps_y", "-1");
cvar_t show_clock = CVAR("cl_clock", "0"); cvar_t show_clock = CVAR("cl_clock", "0");

View file

@ -234,6 +234,7 @@ typedef struct
int server_time; int server_time;
int client_time; int client_time;
float sentgametime; //nq timings are based upon server time echos.
} outframe_t; } outframe_t;
typedef struct typedef struct
@ -759,6 +760,7 @@ typedef struct
// packetentity_t we got. If this is 0, we can't // packetentity_t we got. If this is 0, we can't
// render a frame yet // render a frame yet
int movesequence; // client->server frames int movesequence; // client->server frames
float movesequence_time; // client->server frame timestamp (vs cl.time)
// int spectator; // int spectator;
int autotrack_hint; //the latest hint from the mod, might be negative for invalid. int autotrack_hint; //the latest hint from the mod, might be negative for invalid.
@ -1166,7 +1168,7 @@ void CL_ClampPitch (int pnum);
int CL_ReadFromServer (void); int CL_ReadFromServer (void);
void CL_WriteToServer (usercmd_t *cmd); void CL_WriteToServer (usercmd_t *cmd);
void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps); void CL_BaseMove (usercmd_t *cmd, int pnum, float priortime, float extratime);
int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost); int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost);
@ -1181,7 +1183,7 @@ void CL_UseIndepPhysics(qboolean allow);
void CL_FlushClientCommands(void); void CL_FlushClientCommands(void);
void VARGS CL_SendClientCommand(qboolean reliable, char *format, ...) LIKEPRINTF(2); void VARGS CL_SendClientCommand(qboolean reliable, char *format, ...) LIKEPRINTF(2);
float CL_FilterTime (double time, float wantfps, qboolean ignoreserver); float CL_FilterTime (double time, float wantfps, float limit, qboolean ignoreserver);
int CL_RemoveClientCommands(char *command); int CL_RemoveClientCommands(char *command);
void CL_AllowIndependantSendCmd(qboolean allow); void CL_AllowIndependantSendCmd(qboolean allow);

View file

@ -232,6 +232,7 @@ void CL_GetNumberedEntityInfo (int num, float *org, float *ang)
// FIXME: bmodel issues... // FIXME: bmodel issues...
} }
#ifdef Q2SERVER
void CLQ2_WriteDemoBaselines(sizebuf_t *buf) void CLQ2_WriteDemoBaselines(sizebuf_t *buf)
{ {
int i; int i;
@ -270,6 +271,7 @@ void CLQ2_WriteDemoBaselines(sizebuf_t *buf)
MSGQ2_WriteDeltaEntity(&nullstate, &es, buf, true, true); MSGQ2_WriteDeltaEntity(&nullstate, &es, buf, true, true);
} }
} }
#endif
void CLQ2_ClearState(void) void CLQ2_ClearState(void)
{ {

View file

@ -2107,44 +2107,13 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.Compression) //RLE? BITFIELDS (gah)? if (h.Compression) //RLE? BITFIELDS (gah)?
return NULL; return NULL;
if (length < h.Size)
return NULL; //truncated...
*width = h.Width; *width = h.Width;
*height = h.Height; *height = h.Height;
if (h.NumofColorIndices != 0 || h.BitCount == 8) //8 bit if (h.BitCount == 4) //4 bit
{
int x, y;
unsigned int *data32;
unsigned int pal[256];
if (!h.NumofColorIndices)
h.NumofColorIndices = (int)pow(2, h.BitCount);
if (h.NumofColorIndices>256)
return NULL;
data = buf+2;
data += sizeof(h);
for (i = 0; i < h.NumofColorIndices; i++)
{
pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24);
}
buf += h.OffsetofBMPBits;
data32 = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++)
{
i = (h.Height-1-y) * (h.Width);
for (x = 0; x < h.Width; x++)
{
data32[i] = pal[buf[x]];
i++;
}
buf += h.Width;
}
return (qbyte *)data32;
}
else if (h.BitCount == 4) //4 bit
{ {
int x, y; int x, y;
unsigned int *data32; unsigned int *data32;
@ -2179,6 +2148,39 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
return (qbyte *)data32; return (qbyte *)data32;
} }
else if (h.BitCount == 8) //8 bit
{
int x, y;
unsigned int *data32;
unsigned int pal[256];
if (!h.NumofColorIndices)
h.NumofColorIndices = (int)pow(2, h.BitCount);
if (h.NumofColorIndices>256)
return NULL;
data = buf+2;
data += sizeof(h);
for (i = 0; i < h.NumofColorIndices; i++)
{
pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24);
}
buf += h.OffsetofBMPBits;
data32 = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++)
{
i = (h.Height-1-y) * (h.Width);
for (x = 0; x < h.Width; x++)
{
data32[i] = pal[buf[x]];
i++;
}
buf += h.Width;
}
return (qbyte *)data32;
}
else if (h.BitCount == 24) //24 bit... no 16? else if (h.BitCount == 24) //24 bit... no 16?
{ {
int x, y; int x, y;
@ -2498,8 +2500,8 @@ static qboolean Image_ReadDDSFile(texid_t tex, unsigned int flags, char *fname,
} }
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('2'<<24))) //dx3 with premultiplied alpha else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('2'<<24))) //dx3 with premultiplied alpha
{ {
if (!(tex->flags & IF_PREMULTIPLYALPHA)) // if (!(tex->flags & IF_PREMULTIPLYALPHA))
return false; // return false;
encoding = PTI_S3RGBA3; encoding = PTI_S3RGBA3;
pad = 8; pad = 8;
divsize = 4; divsize = 4;
@ -2507,8 +2509,8 @@ static qboolean Image_ReadDDSFile(texid_t tex, unsigned int flags, char *fname,
} }
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))) else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24)))
{ {
if (tex->flags & IF_PREMULTIPLYALPHA) // if (tex->flags & IF_PREMULTIPLYALPHA)
return false; // return false;
encoding = PTI_S3RGBA3; encoding = PTI_S3RGBA3;
pad = 8; pad = 8;
divsize = 4; divsize = 4;
@ -2517,7 +2519,8 @@ static qboolean Image_ReadDDSFile(texid_t tex, unsigned int flags, char *fname,
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('4'<<24))) //dx5 with premultiplied alpha else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('4'<<24))) //dx5 with premultiplied alpha
{ {
// if (!(tex->flags & IF_PREMULTIPLYALPHA)) // if (!(tex->flags & IF_PREMULTIPLYALPHA))
return false; // return false;
encoding = PTI_S3RGBA5; encoding = PTI_S3RGBA5;
pad = 8; pad = 8;
divsize = 4; divsize = 4;
@ -2525,8 +2528,8 @@ static qboolean Image_ReadDDSFile(texid_t tex, unsigned int flags, char *fname,
} }
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24))) else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)))
{ {
if (tex->flags & IF_PREMULTIPLYALPHA) // if (tex->flags & IF_PREMULTIPLYALPHA)
return false; // return false;
encoding = PTI_S3RGBA5; encoding = PTI_S3RGBA5;
pad = 8; pad = 8;
@ -4410,8 +4413,7 @@ qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname,
return false; return false;
} }
//loads from a single mip. takes ownership of the data. struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicename, char *subpath, unsigned int texflags)
static qboolean Image_LoadCubemapTexture(texid_t tex, char *nicename)
{ {
static struct static struct
{ {
@ -4446,6 +4448,8 @@ static qboolean Image_LoadCubemapTexture(texid_t tex, char *nicename)
size_t filesize; size_t filesize;
int width, height; int width, height;
qboolean hasalpha; qboolean hasalpha;
char *nextprefix, *prefixend;
size_t prefixlen;
mips = Z_Malloc(sizeof(*mips)); mips = Z_Malloc(sizeof(*mips));
mips->type = PTI_CUBEMAP; mips->type = PTI_CUBEMAP;
mips->mipcount = 6; mips->mipcount = 6;
@ -4454,51 +4458,79 @@ static qboolean Image_LoadCubemapTexture(texid_t tex, char *nicename)
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
{ {
for (e = (tex->flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++) prefixlen = 0;
nextprefix = subpath;
for(;;)
{ {
//try and open one for (e = (texflags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++)
qbyte *buf = NULL, *data;
filesize = 0;
for (j = 0; j < sizeof(cmscheme)/sizeof(cmscheme[0])/6; j++)
{ {
Q_snprintfz(fname, sizeof(fname), "%s%s%s", nicename, cmscheme[i + 6*j].suffix, tex_extensions[e].name); //try and open one
buf = COM_LoadFile(fname, 5, &filesize); qbyte *buf = NULL, *data;
if (buf) filesize = 0;
break;
}
//now read it for (j = 0; j < sizeof(cmscheme)/sizeof(cmscheme[0])/6; j++)
if (buf)
{
if ((data = Read32BitImageFile(buf, filesize, &width, &height, &hasalpha, fname)))
{ {
extern cvar_t vid_hardwaregamma; Q_snprintfz(fname+prefixlen, sizeof(fname)-prefixlen, "%s_%s%s", nicename, cmscheme[i + 6*j].suffix, tex_extensions[e].name);
if (!(tex->flags&IF_NOGAMMA) && !vid_hardwaregamma.value) buf = COM_LoadFile(fname, 5, &filesize);
BoostGamma(data, width, height); if (buf)
mips->mip[i].data = R_FlipImage32(data, &width, &height, cmscheme[i + 6*j].flipx, cmscheme[i + 6*j].flipy, cmscheme[i + 6*j].flipd); break;
mips->mip[i].datasize = width*height*4;
mips->mip[i].width = width;
mips->mip[i].height = height;
mips->mip[i].needfree = true;
if (i == 0) Q_snprintfz(fname+prefixlen, sizeof(fname)-prefixlen, "%s%s%s", nicename, cmscheme[i + 6*j].suffix, tex_extensions[e].name);
buf = COM_LoadFile(fname, 5, &filesize);
if (buf)
break;
}
//now read it
if (buf)
{
if ((data = Read32BitImageFile(buf, filesize, &width, &height, &hasalpha, fname)))
{ {
tex->width = width; extern cvar_t vid_hardwaregamma;
tex->height = height; if (width == height && (!i || width == mips->mip[0].width)) //cubemaps must be square and all the same size (npot is fine though)
{ //(skies have a fallback for invalid sizes, but it'll run a bit slower)
if (!(texflags&IF_NOGAMMA) && !vid_hardwaregamma.value)
BoostGamma(data, width, height);
mips->mip[i].data = R_FlipImage32(data, &width, &height, cmscheme[i + 6*j].flipx, cmscheme[i + 6*j].flipy, cmscheme[i + 6*j].flipd);
mips->mip[i].datasize = width*height*4;
mips->mip[i].width = width;
mips->mip[i].height = height;
mips->mip[i].needfree = true;
BZ_Free(buf);
goto nextface;
}
BZ_Free(data);
} }
BZ_Free(buf); BZ_Free(buf);
break;
} }
BZ_Free(buf);
} }
}
}
if (tex->flags & IF_NOWORKER) //get ready for the next prefix...
Image_LoadTextureMips(tex, mips, 0, 0); if (!nextprefix || !*nextprefix)
else break; //no more...
COM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0); prefixend = strchr(nextprefix, ':');
return true; if (!prefixend)
prefixend = nextprefix+strlen(nextprefix);
prefixlen = prefixend-nextprefix;
if (prefixlen >= sizeof(fname)-2)
prefixlen = sizeof(fname)-2;
memcpy(fname, nextprefix, prefixlen);
fname[prefixlen++] = '/';
if (*prefixend)
prefixend++;
nextprefix = prefixend;
}
while(i>0)
BZ_Free(mips->mip[i--].data);
Z_Free(mips);
return NULL;
nextface:;
}
return mips;
} }
static qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *bestname, size_t bestnamesize, unsigned int *bestflags) static qboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *bestname, size_t bestnamesize, unsigned int *bestflags)
@ -4740,6 +4772,9 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t
//the exception is single-file dds cubemaps, but we don't support those. //the exception is single-file dds cubemaps, but we don't support those.
for(altname = tex->ident;altname;altname = nextalt) for(altname = tex->ident;altname;altname = nextalt)
{ {
char *prefixes[] = {"textures/", "", NULL};
struct pendingtextureinfo *mips;
nextalt = strchr(altname, ':'); nextalt = strchr(altname, ':');
if (nextalt) if (nextalt)
{ {
@ -4751,14 +4786,23 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t
altname = fname; altname = fname;
} }
if (!Image_LoadCubemapTexture(tex, altname)) mips = Image_LoadCubemapTextureData(altname, tex->subpath, tex->flags);
if (mips)
{ {
tex->width = mips->mip[0].width;
tex->height = mips->mip[0].height;
if (tex->flags & IF_NOWORKER) if (tex->flags & IF_NOWORKER)
Image_LoadTexture_Failed(tex, NULL, 0, 0); Image_LoadTextureMips(tex, mips, 0, 0);
else else
COM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0); COM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0);
return;
} }
} }
if (tex->flags & IF_NOWORKER)
Image_LoadTexture_Failed(tex, NULL, 0, 0);
else
COM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0);
return; return;
} }

View file

@ -758,6 +758,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
mouse_y *= sensitivity.value*in_sensitivityscale; mouse_y *= sensitivity.value*in_sensitivityscale;
} }
/*
#ifdef QUAKESTATS #ifdef QUAKESTATS
if (cl.playerview[pnum].statsf[STAT_VIEWZOOM]) if (cl.playerview[pnum].statsf[STAT_VIEWZOOM])
{ {
@ -765,6 +766,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
mouse_y *= cl.playerview[pnum].statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE; mouse_y *= cl.playerview[pnum].statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE;
} }
#endif #endif
*/
if (!movements) if (!movements)
{ {

View file

@ -3211,6 +3211,10 @@ static void P_ImportEffectInfo(char *config, char *line)
Con_DPrintf("Particle effect token %s not supported\n", arg[0]); Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainless") && args == 2) else if (!strcmp(arg[0], "stainless") && args == 2)
Con_DPrintf("Particle effect token %s not supported\n", arg[0]); Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "relativeoriginoffset") && args == 4)
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "relativevelocityoffset") && args == 4)
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
#endif #endif
else if (!strcmp(arg[0], "rotate") && args == 5) else if (!strcmp(arg[0], "rotate") && args == 5)
{ {

View file

@ -43,6 +43,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "pr_common.h" #include "pr_common.h"
extern usercmd_t cl_pendingcmd[MAX_SPLITS];
#ifndef TEXTEDITOR #ifndef TEXTEDITOR
//client only builds don't have a qc debugger //client only builds don't have a qc debugger
#define QCEditor NULL #define QCEditor NULL
@ -68,7 +71,11 @@ world_t csqc_world;
int csqc_playerseat; //can be negative. int csqc_playerseat; //can be negative.
static playerview_t *csqc_playerview; static playerview_t *csqc_playerview;
qboolean csqc_dp_lastwas3d; //to emulate DP correctly, we need to track whether drawpic/drawfill or clearscene was called last. blame 515. qboolean csqc_dp_lastwas3d; //to emulate DP correctly, we need to track whether drawpic/drawfill or clearscene was called last. blame 515.
#ifdef NOLEGACY
#define csqc_isdarkplaces false //hopefully this will allow a smart enough compiler to optimise it out cleanly
#else
static qboolean csqc_isdarkplaces; static qboolean csqc_isdarkplaces;
#endif
static qboolean csqc_singlecheats; /*single player or cheats active, allowing custom addons*/ static qboolean csqc_singlecheats; /*single player or cheats active, allowing custom addons*/
static qboolean csqc_mayread; //csqc is allowed to ReadByte(); static qboolean csqc_mayread; //csqc is allowed to ReadByte();
static qboolean csqc_worldchanged; //make sure any caches are rebuilt properly before the next renderscene static qboolean csqc_worldchanged; //make sure any caches are rebuilt properly before the next renderscene
@ -98,6 +105,19 @@ extern sfx_t *cl_sfx_ric2;
extern sfx_t *cl_sfx_ric3; extern sfx_t *cl_sfx_ric3;
extern sfx_t *cl_sfx_r_exp3; extern sfx_t *cl_sfx_r_exp3;
#define ENDLIST
#ifdef NOLEGACY
#define legacycsqcglobals
#else
#define legacycsqcglobals \
globalstring(trace_dphittexturename, "trace_dphittexturename"); /*for dp compat*/ \
globalfloat(trace_dpstartcontents, "trace_dpstartcontents"); /*for dp compat*/ \
globalfloat(trace_dphitcontents, "trace_dphitcontents"); /*for dp compat*/ \
globalfloat(trace_dphitq3surfaceflags, "trace_dphitq3surfaceflags"); /*for dp compat*/ \
globalfloat(trace_surfaceflagsf, "trace_surfaceflagsf"); /*float written by traceline, for mods that lack ints*/ \
globalfloat(trace_endcontentsf, "trace_endcontentsf"); /*float written by traceline EXT_CSQC_1, for mods that lack ints*/ \
ENDLIST
#endif
//If I do it like this, I'll never forget to register something... //If I do it like this, I'll never forget to register something...
#define csqcglobals \ #define csqcglobals \
@ -119,6 +139,7 @@ extern sfx_t *cl_sfx_r_exp3;
globalfunction(console_link, "CSQC_ConsoleLink"); \ globalfunction(console_link, "CSQC_ConsoleLink"); \
globalfunction(gamecommand, "GameCommand"); /*DP extension*/\ globalfunction(gamecommand, "GameCommand"); /*DP extension*/\
\ \
globalfunction(ent_spawn, "CSQC_Ent_Spawn"); \
globalfunction(ent_update, "CSQC_Ent_Update"); \ globalfunction(ent_update, "CSQC_Ent_Update"); \
globalfunction(ent_remove, "CSQC_Ent_Remove"); \ globalfunction(ent_remove, "CSQC_Ent_Remove"); \
\ \
@ -158,16 +179,15 @@ extern sfx_t *cl_sfx_r_exp3;
globalvector(trace_plane_normal, "trace_plane_normal"); /*vector written by traceline*/ \ globalvector(trace_plane_normal, "trace_plane_normal"); /*vector written by traceline*/ \
globalfloat(trace_plane_dist, "trace_plane_dist"); /*float written by traceline*/ \ globalfloat(trace_plane_dist, "trace_plane_dist"); /*float written by traceline*/ \
globalentity(trace_ent, "trace_ent"); /*entity written by traceline*/ \ globalentity(trace_ent, "trace_ent"); /*entity written by traceline*/ \
globalfloat(trace_surfaceflagsf, "trace_surfaceflagsf"); /*float written by traceline*/ \
globalint(trace_surfaceflagsi, "trace_surfaceflagsi"); /*int written by traceline*/ \ globalint(trace_surfaceflagsi, "trace_surfaceflagsi"); /*int written by traceline*/ \
globalstring(trace_surfacename, "trace_surfacename"); /*string written by traceline*/ \ globalstring(trace_surfacename, "trace_surfacename"); /*string written by traceline*/ \
globalfloat(trace_endcontentsf, "trace_endcontentsf"); /*float written by traceline EXT_CSQC_1*/ \
globalint(trace_endcontentsi, "trace_endcontentsi"); /*int written by traceline EXT_CSQC_1*/ \ globalint(trace_endcontentsi, "trace_endcontentsi"); /*int written by traceline EXT_CSQC_1*/ \
globalint(trace_brush_id, "trace_brush_id"); /*int written by traceline*/ \ globalint(trace_brush_id, "trace_brush_id"); /*int written by traceline*/ \
globalint(trace_brush_faceid, "trace_brush_faceid"); /*int written by traceline*/ \ globalint(trace_brush_faceid, "trace_brush_faceid"); /*int written by traceline*/ \
globalint(trace_surface_id, "trace_surface_id"); /*int written by traceline*/ \ globalint(trace_surface_id, "trace_surface_id"); /*int written by traceline*/ \
globalint(trace_bone_id, "trace_bone_id"); /*int written by traceline*/ \ globalint(trace_bone_id, "trace_bone_id"); /*int written by traceline*/ \
globalint(trace_triangle_id, "trace_triangle_id"); /*int written by traceline*/ \ globalint(trace_triangle_id, "trace_triangle_id"); /*int written by traceline*/ \
legacycsqcglobals \
\ \
globalfloat(clientcommandframe, "clientcommandframe"); /*float the next frame that will be sent*/ \ globalfloat(clientcommandframe, "clientcommandframe"); /*float the next frame that will be sent*/ \
globalfloat(servercommandframe, "servercommandframe"); /*float the most recent frame received from the server*/ \ globalfloat(servercommandframe, "servercommandframe"); /*float the most recent frame received from the server*/ \
@ -183,6 +203,7 @@ extern sfx_t *cl_sfx_r_exp3;
globalvector(pmove_maxs, "pmove_maxs"); /*deprecated. read/written by runplayerphysics*/ \ globalvector(pmove_maxs, "pmove_maxs"); /*deprecated. read/written by runplayerphysics*/ \
globalfloat(pmove_jump_held, "pmove_jump_held"); /*deprecated. read/written by runplayerphysics*/ \ globalfloat(pmove_jump_held, "pmove_jump_held"); /*deprecated. read/written by runplayerphysics*/ \
globalfloat(pmove_waterjumptime, "pmove_waterjumptime"); /*deprecated. read/written by runplayerphysics*/ \ globalfloat(pmove_waterjumptime, "pmove_waterjumptime"); /*deprecated. read/written by runplayerphysics*/ \
globalfloat(pmove_onground, "pmove_onground"); /*deprecated. read/written by runplayerphysics*/ \
\ \
globalfloat(input_timelength, "input_timelength"); /*float filled by getinputstate, read by runplayerphysics*/ \ globalfloat(input_timelength, "input_timelength"); /*float filled by getinputstate, read by runplayerphysics*/ \
globalvector(input_angles, "input_angles"); /*vector filled by getinputstate, read by runplayerphysics*/ \ globalvector(input_angles, "input_angles"); /*vector filled by getinputstate, read by runplayerphysics*/ \
@ -203,7 +224,7 @@ extern sfx_t *cl_sfx_r_exp3;
globalfloat(autocvar_vid_conwidth, "autocvar_vid_conwidth"); /*float hackfix for dp mods*/ \ globalfloat(autocvar_vid_conwidth, "autocvar_vid_conwidth"); /*float hackfix for dp mods*/ \
globalfloat(autocvar_vid_conheight, "autocvar_vid_conheight"); /*float hackfix for dp mods*/ \ globalfloat(autocvar_vid_conheight, "autocvar_vid_conheight"); /*float hackfix for dp mods*/ \
globalfloat(cycle_wrapped, "cycle_wrapped"); \ globalfloat(cycle_wrapped, "cycle_wrapped"); \
ENDLIST
typedef struct { typedef struct {
#define globalfloat(name,qcname) float *name #define globalfloat(name,qcname) float *name
@ -265,30 +286,33 @@ static void CSQC_ChangeLocalPlayer(int seat)
} }
if ((unsigned int)seat < MAX_SPLITS) if ((unsigned int)seat < MAX_SPLITS)
{ {
extern usercmd_t independantphysics[MAX_SPLITS]; // int i;
int i; usercmd_t *cmd = &cl_pendingcmd[seat];
usercmd_t *cmd = &independantphysics[seat]; // usercmd_t tmp;
usercmd_t tmp; // for (i=0 ; i<3 ; i++)
for (i=0 ; i<3 ; i++) // cmd->angles[i] = ((int)(csqc_playerview->viewangles[i]*65536.0/360)&65535);
cmd->angles[i] = ((int)(csqc_playerview->viewangles[i]*65536.0/360)&65535); // if (!cmd->msec)
if (!cmd->msec) // CL_BaseMove (cmd, seat, cmd->msec, newtime);
CL_BaseMove (cmd, seat, 0, 72); // tmp = *cmd;
tmp = *cmd; // cmd = &tmp;
cmd = &tmp; // cmd->msec = (realtime - cl.outframes[(cl.movesequence-1)&UPDATE_MASK].senttime)*1000;
cmd->msec = (realtime - cl.outframes[(cl.movesequence-1)&UPDATE_MASK].senttime)*1000;
cs_set_input_state(cmd); cs_set_input_state(cmd);
if (csqcg.pmove_org) if (csqcg.pmove_org)
{ VectorCopy(csqc_playerview->simorg, csqcg.pmove_org);
csqcg.pmove_org[0] = csqc_playerview->simorg[0]; if (csqc_isdarkplaces)
csqcg.pmove_org[1] = csqc_playerview->simorg[1]; { //dp mods tend to require these to be totally unlerped
csqcg.pmove_org[2] = csqc_playerview->simorg[2]; if (csqcg.pmove_vel)
VectorCopy(cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[csqc_playerview->playernum].velocity, csqcg.pmove_vel);
if (csqcg.pmove_onground)
*csqcg.pmove_onground = cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[csqc_playerview->playernum].onground;
} }
if (csqcg.pmove_vel) else
{ {
csqcg.pmove_vel[0] = csqc_playerview->simvel[0]; if (csqcg.pmove_vel)
csqcg.pmove_vel[1] = csqc_playerview->simvel[1]; VectorCopy(csqc_playerview->simvel, csqcg.pmove_vel);
csqcg.pmove_vel[2] = csqc_playerview->simvel[2]; if (csqcg.pmove_onground)
*csqcg.pmove_onground = csqc_playerview->onground;
} }
} }
} }
@ -320,6 +344,15 @@ static void CSQC_FindGlobals(qboolean nofuncs)
#define ensurevector(name) if (!csqcg.name) csqcg.name = junk._vector; #define ensurevector(name) if (!csqcg.name) csqcg.name = junk._vector;
#define ensureentity(name) if (!csqcg.name) csqcg.name = &junk.edict; #define ensureentity(name) if (!csqcg.name) csqcg.name = &junk.edict;
#ifdef NOLEGACY
{
etype_t etype = ev_void;
if (!csqcg.trace_surfaceflagsi)
csqcg.trace_surfaceflagsi = PR_FindGlobal(csqcprogs, "trace_surfaceflags", 0, &etype);
if (!csqcg.trace_endcontentsi)
csqcg.trace_endcontentsi = PR_FindGlobal(csqcprogs, "trace_endcontents", 0, &etype);
}
#else
if (!csqcg.trace_surfaceflagsf && !csqcg.trace_surfaceflagsi) if (!csqcg.trace_surfaceflagsf && !csqcg.trace_surfaceflagsi)
{ {
etype_t etype = ev_void; etype_t etype = ev_void;
@ -338,6 +371,9 @@ static void CSQC_FindGlobals(qboolean nofuncs)
else if (etype == ev_integer) else if (etype == ev_integer)
csqcg.trace_endcontentsi = &v->_int; csqcg.trace_endcontentsi = &v->_int;
} }
ensurefloat(trace_surfaceflagsf);
ensurefloat(trace_endcontentsf);
#endif
ensurefloat(trace_allsolid); ensurefloat(trace_allsolid);
ensurefloat(trace_startsolid); ensurefloat(trace_startsolid);
@ -347,9 +383,7 @@ static void CSQC_FindGlobals(qboolean nofuncs)
ensurevector(trace_endpos); ensurevector(trace_endpos);
ensurevector(trace_plane_normal); ensurevector(trace_plane_normal);
ensurefloat(trace_plane_dist); ensurefloat(trace_plane_dist);
ensurefloat(trace_surfaceflagsf);
ensureint(trace_surfaceflagsi); ensureint(trace_surfaceflagsi);
ensurefloat(trace_endcontentsf);
ensureint(trace_endcontentsi); ensureint(trace_endcontentsi);
ensureint(trace_brush_id); ensureint(trace_brush_id);
ensureint(trace_brush_faceid); ensureint(trace_brush_faceid);
@ -456,7 +490,12 @@ typedef struct csqcedict_s
}; };
#endif #endif
/*the above is shared with qclib*/ /*the above is shared with qclib*/
#ifdef USEAREAGRID
areagridlink_t gridareas[AREAGRIDPERENT]; //on overflow, use the inefficient overflow list.
size_t gridareasequence; //used to avoid iterrating the same ent twice.
#else
link_t area; link_t area;
#endif
pvscache_t pvsinfo; pvscache_t pvsinfo;
int lastruntime; int lastruntime;
int solidsize; int solidsize;
@ -866,7 +905,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
out->topcolour = cl.players[ival-1].ttopcolor; out->topcolour = cl.players[ival-1].ttopcolor;
out->bottomcolour = cl.players[ival-1].tbottomcolor; out->bottomcolour = cl.players[ival-1].tbottomcolor;
} }
else if (ival >= 1024) else if (ival /*>= 1024*/)
{ {
//DP COLORMAP extension //DP COLORMAP extension
out->topcolour = (ival>>4) & 0x0f; out->topcolour = (ival>>4) & 0x0f;
@ -879,29 +918,24 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
} }
if (!in->xv->colormod[0] && !in->xv->colormod[1] && !in->xv->colormod[2]) if (!in->xv->colormod[0] && !in->xv->colormod[1] && !in->xv->colormod[2])
{ VectorSet(out->shaderRGBAf, 1, 1, 1);
out->shaderRGBAf[0] = 1;
out->shaderRGBAf[1] = 1;
out->shaderRGBAf[2] = 1;
}
else else
{ {
out->flags |= RF_FORCECOLOURMOD; out->flags |= RF_FORCECOLOURMOD;
out->shaderRGBAf[0] = in->xv->colormod[0]; VectorCopy(in->xv->colormod, out->shaderRGBAf);
out->shaderRGBAf[1] = in->xv->colormod[1];
out->shaderRGBAf[2] = in->xv->colormod[2];
} }
if (!in->xv->alpha || in->xv->alpha == 1) if (!in->xv->alpha || in->xv->alpha == 1)
{
out->shaderRGBAf[3] = 1.0f; out->shaderRGBAf[3] = 1.0f;
}
else else
{ {
out->flags |= RF_TRANSLUCENT; out->flags |= RF_TRANSLUCENT;
out->shaderRGBAf[3] = in->xv->alpha; out->shaderRGBAf[3] = in->xv->alpha;
} }
VectorCopy(in->xv->glowmod, out->glowmod); if (!in->xv->glowmod[0] && !in->xv->glowmod[1] && !in->xv->glowmod[2])
VectorSet(out->glowmod, 1, 1, 1);
else
VectorCopy(in->xv->glowmod, out->glowmod);
#ifdef HEXEN2 #ifdef HEXEN2
out->drawflags = in->xv->drawflags; out->drawflags = in->xv->drawflags;
@ -1217,6 +1251,7 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva
int maxe; int maxe;
int oldself = *csqcg.self; int oldself = *csqcg.self;
RSpeedMark();
if (cl.worldmodel) if (cl.worldmodel)
{ {
@ -1307,6 +1342,8 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva
CL_UpdateTEnts (); CL_UpdateTEnts ();
} }
} }
RSpeedEnd(RSPEED_LINKENTITIES);
} }
//enum {vb_vertexcoord, vb_texcoord, vb_rgba, vb_normal, vb_sdir, vb_tdir, vb_indexes, vb_rgb, vb_alpha}; //enum {vb_vertexcoord, vb_texcoord, vb_rgba, vb_normal, vb_sdir, vb_tdir, vb_indexes, vb_rgb, vb_alpha};
@ -1385,8 +1422,10 @@ static void CSQC_PolyFlush(void)
// #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???) // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???)
void QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
shader_t *shader; const char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);
int qcflags = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0; int qcflags = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;
shader_t *shader;
extern shader_t *shader_draw_fill_trans;
int beflags; int beflags;
qboolean twod; qboolean twod;
@ -1407,10 +1446,12 @@ void QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr
if (csqc_isdarkplaces || (qcflags & DRAWFLAG_TWOSIDED)) if (csqc_isdarkplaces || (qcflags & DRAWFLAG_TWOSIDED))
beflags |= BEF_FORCETWOSIDED; beflags |= BEF_FORCETWOSIDED;
if (twod) if (!*shadername)
shader = R_RegisterPic(PR_GetStringOfs(prinst, OFS_PARM0), NULL); shader = shader_draw_fill_trans; //dp compat...
else if (twod)
shader = R_RegisterPic(shadername, NULL);
else else
shader = R_RegisterSkin(PR_GetStringOfs(prinst, OFS_PARM0), NULL); shader = R_RegisterSkin(shadername, NULL);
if (R2D_Flush && (R2D_Flush != CSQC_PolyFlush || csqc_poly_shader != shader || csqc_poly_flags != beflags || csqc_poly_2d != twod)) if (R2D_Flush && (R2D_Flush != CSQC_PolyFlush || csqc_poly_shader != shader || csqc_poly_flags != beflags || csqc_poly_2d != twod))
R2D_Flush(); R2D_Flush();
@ -2361,11 +2402,9 @@ static void cs_settracevars(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
VectorCopy (tr->endpos, csqcg.trace_endpos); VectorCopy (tr->endpos, csqcg.trace_endpos);
VectorCopy (tr->plane.normal, csqcg.trace_plane_normal); VectorCopy (tr->plane.normal, csqcg.trace_plane_normal);
*csqcg.trace_plane_dist = tr->plane.dist; *csqcg.trace_plane_dist = tr->plane.dist;
*csqcg.trace_surfaceflagsf = tr->surface?tr->surface->flags:0;
*csqcg.trace_surfaceflagsi = tr->surface?tr->surface->flags:0; *csqcg.trace_surfaceflagsi = tr->surface?tr->surface->flags:0;
if (csqcg.trace_surfacename) if (csqcg.trace_surfacename)
prinst->SetStringField(prinst, NULL, csqcg.trace_surfacename, tr->surface?tr->surface->name:NULL, true); prinst->SetStringField(prinst, NULL, csqcg.trace_surfacename, tr->surface?tr->surface->name:NULL, true);
*csqcg.trace_endcontentsf = tr->contents;
*csqcg.trace_endcontentsi = tr->contents; *csqcg.trace_endcontentsi = tr->contents;
*csqcg.trace_brush_id = tr->brush_id; *csqcg.trace_brush_id = tr->brush_id;
*csqcg.trace_brush_faceid = tr->brush_face; *csqcg.trace_brush_faceid = tr->brush_face;
@ -2376,6 +2415,20 @@ static void cs_settracevars(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)tr->ent); *csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)tr->ent);
else else
*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)csqc_world.edicts); *csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)csqc_world.edicts);
#ifndef NOLEGACY
*csqcg.trace_endcontentsf = tr->contents;
*csqcg.trace_surfaceflagsf = tr->surface?tr->surface->flags:0;
if (csqcg.trace_dphittexturename)
prinst->SetStringField(prinst, NULL, csqcg.trace_dphittexturename, tr->surface?tr->surface->name:NULL, true);
if (csqcg.trace_dpstartcontents)
*csqcg.trace_dpstartcontents = FTEToDPContents(0); //fixme, maybe
if (csqcg.trace_dphitcontents)
*csqcg.trace_dphitcontents = FTEToDPContents(tr->contents);
if (csqcg.trace_dphitq3surfaceflags)
*csqcg.trace_dphitq3surfaceflags = tr->surface?tr->surface->flags:0;
#endif
} }
static void QCBUILTIN PF_cs_traceline(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_cs_traceline(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -3091,7 +3144,7 @@ static void cs_set_input_state (usercmd_t *cmd)
if (csqcg.input_weapon) if (csqcg.input_weapon)
*csqcg.input_weapon = cmd->weapon; *csqcg.input_weapon = cmd->weapon;
if (csqcg.input_servertime) if (csqcg.input_servertime)
*csqcg.input_servertime = cmd->servertime/1000.0f; *csqcg.input_servertime = cmd->fservertime;
if (csqcg.input_clienttime) if (csqcg.input_clienttime)
*csqcg.input_clienttime = cmd->fclienttime/1000.0f; *csqcg.input_clienttime = cmd->fclienttime/1000.0f;
@ -3134,7 +3187,7 @@ static void cs_get_input_state (usercmd_t *cmd)
if (csqcg.input_weapon) if (csqcg.input_weapon)
cmd->weapon = *csqcg.input_weapon; cmd->weapon = *csqcg.input_weapon;
if (csqcg.input_servertime) if (csqcg.input_servertime)
cmd->servertime = *csqcg.input_servertime*1000; cmd->fservertime = *csqcg.input_servertime;
if (csqcg.input_cursor_screen) if (csqcg.input_cursor_screen)
Vector2Copy(csqcg.input_cursor_screen, cmd->cursor_screen); Vector2Copy(csqcg.input_cursor_screen, cmd->cursor_screen);
@ -3149,8 +3202,8 @@ static void cs_get_input_state (usercmd_t *cmd)
//get the input commands, and stuff them into some globals. //get the input commands, and stuff them into some globals.
static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
usercmd_t *cmd, tmp; usercmd_t *cmd;
extern usercmd_t independantphysics[MAX_SPLITS]; extern usercmd_t cl_pendingcmd[MAX_SPLITS];
int f = G_FLOAT(OFS_PARM0); int f = G_FLOAT(OFS_PARM0);
int seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat); int seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat);
@ -3170,9 +3223,10 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global
/*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/ /*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/
if (f == cl.movesequence) if (f == cl.movesequence)
{ {
int i; // int i;
cmd = &independantphysics[seat]; // usercmd_t tmp;
cmd = &cl_pendingcmd[seat];
/*
tmp = *cmd; tmp = *cmd;
cmd = &tmp; cmd = &tmp;
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
@ -3180,9 +3234,13 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global
if (!cmd->msec) if (!cmd->msec)
{ {
// *cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat]; // *cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat];
CL_BaseMove (cmd, seat, 0, 72); CL_BaseMove (cmd, seat, cmd->msec, newtime);
} }
cmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000; // if (cl.predservertimes)
// cmd->msec = (cl.time - cl.outframes[(f-1)&UPDATE_MASK].cmd[seat].fservertime)*1000;
// else
cmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000;
*/
} }
else else
{ {
@ -3203,7 +3261,7 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global
//not intended to affect client state at all //not intended to affect client state at all
static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
unsigned int msecs; float msecs;
float oldtime = *csqcg.simtime; float oldtime = *csqcg.simtime;
csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);
@ -3228,7 +3286,7 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo
pmove.jump_msec = 0;//(cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec; pmove.jump_msec = 0;//(cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec;
//set up the movement command //set up the movement command
msecs = *csqcg.input_timelength*1000 + 0.5f; msecs = *csqcg.input_timelength*1000;
//precision inaccuracies. :( //precision inaccuracies. :(
pmove.cmd.angles[0] = ANGLE2SHORT(csqcg.input_angles[0]); pmove.cmd.angles[0] = ANGLE2SHORT(csqcg.input_angles[0]);
pmove.cmd.angles[1] = ANGLE2SHORT(csqcg.input_angles[1]); pmove.cmd.angles[1] = ANGLE2SHORT(csqcg.input_angles[1]);
@ -3270,7 +3328,7 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo
CL_SetSolidEntities(); CL_SetSolidEntities();
while(msecs) //break up longer commands while(msecs > 0) //break up longer commands
{ {
pmove.cmd.msec = msecs; pmove.cmd.msec = msecs;
if (pmove.cmd.msec > 50) if (pmove.cmd.msec > 50)
@ -3587,8 +3645,17 @@ static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars
else else
G_INT(OFS_RETURN) = 0; G_INT(OFS_RETURN) = 0;
} }
static void QCBUILTIN PF_cs_serverkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *keyname = PR_GetStringOfs(prinst, OFS_PARM0);
const char *ret = PF_cs_serverkey_internal(keyname);
if (*ret)
G_FLOAT(OFS_RETURN) = strtod(ret, NULL);
else
G_FLOAT(OFS_RETURN) = (prinst->callargc >= 2)?G_FLOAT(OFS_PARM1):0;
}
//string(float pnum, string keyname) //string(float pnum, string keyname)
static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_cs_getplayerkeystring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
int pnum = G_FLOAT(OFS_PARM0); int pnum = G_FLOAT(OFS_PARM0);
const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);
@ -3612,6 +3679,30 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv
else else
G_INT(OFS_RETURN) = 0; G_INT(OFS_RETURN) = 0;
} }
static void QCBUILTIN PF_cs_getplayerkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int pnum = G_FLOAT(OFS_PARM0);
const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);
const char *ret;
if (pnum < 0)
{
if (csqc_resortfrags)
{
Sbar_SortFrags(true, false);
csqc_resortfrags = false;
}
if (pnum >= -scoreboardlines)
{//sort by
pnum = fragsort[-(pnum+1)];
}
}
ret = PF_cs_getplayerkey_internal(pnum, keyname);
if (*ret)
G_FLOAT(OFS_RETURN) = strtod(ret, NULL);
else
G_FLOAT(OFS_RETURN) = (prinst->callargc >= 3)?G_FLOAT(OFS_PARM2):0;
}
static void QCBUILTIN PF_cs_infokey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_cs_infokey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
@ -4873,7 +4964,8 @@ qboolean CSQC_DeltaUpdate(entity_state_t *src)
else else
{ {
ent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0); ent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0);
ent->xv->drawmask = MASK_DELTA; if (!csqc_isdarkplaces)
ent->xv->drawmask = MASK_DELTA;
} }
CSQC_EntStateToCSQC(deltaflags[src->modelindex], csqcdelta_time, src, ent); CSQC_EntStateToCSQC(deltaflags[src->modelindex], csqcdelta_time, src, ent);
@ -6000,7 +6092,8 @@ static struct {
{"runstandardplayerphysics",PF_cs_runplayerphysics, 347}, // #347 void() runstandardplayerphysics (EXT_CSQC) {"runstandardplayerphysics",PF_cs_runplayerphysics, 347}, // #347 void() runstandardplayerphysics (EXT_CSQC)
{"getplayerkeyvalue", PF_cs_getplayerkey, 348}, // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC) {"getplayerkeyvalue", PF_cs_getplayerkeystring, 348}, // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
{"getplayerkeyfloat", PF_cs_getplayerkeyfloat, 0}, // #348 string(float playernum, string keyname) getplayerkeyvalue
{"isdemo", PF_cl_playingdemo, 349}, // #349 float() isdemo (EXT_CSQC) {"isdemo", PF_cl_playingdemo, 349}, // #349 float() isdemo (EXT_CSQC)
//350 //350
@ -6012,6 +6105,7 @@ static struct {
{"wasfreed", PF_WasFreed, 353}, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too) {"wasfreed", PF_WasFreed, 353}, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
{"serverkey", PF_cs_serverkey, 354}, // #354 string(string key) serverkey; {"serverkey", PF_cs_serverkey, 354}, // #354 string(string key) serverkey;
{"serverkeyfloat", PF_cs_serverkeyfloat, 0}, // #0 float(string key) serverkeyfloat;
{"getentitytoken", PF_cs_getentitytoken, 355}, // #355 string() getentitytoken; {"getentitytoken", PF_cs_getentitytoken, 355}, // #355 string() getentitytoken;
{"findfont", PF_CL_findfont, 356}, {"findfont", PF_CL_findfont, 356},
{"loadfont", PF_CL_loadfont, 357}, {"loadfont", PF_CL_loadfont, 357},
@ -6521,6 +6615,8 @@ static model_t *QDECL CSQC_World_ModelForIndex(world_t *w, int modelindex)
model_t *mod = CSQC_GetModelForIndex(modelindex); model_t *mod = CSQC_GetModelForIndex(modelindex);
if (mod && mod->loadstate != MLS_LOADED) if (mod && mod->loadstate != MLS_LOADED)
{ {
if (mod->loadstate == MLS_NOTLOADED)
Mod_LoadModel(mod, MLV_SILENT);
if (mod->loadstate == MLS_LOADING) if (mod->loadstate == MLS_LOADING)
COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);
if (mod->loadstate != MLS_LOADED) if (mod->loadstate != MLS_LOADED)
@ -6831,6 +6927,7 @@ void ASMCALL CSQC_ThinkTimeOp(pubprogfuncs_t *progs, edict_t *ed, float var)
pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc) pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc)
{ {
#ifndef csqc_isdarkplaces
if (!num) if (!num)
{ {
if (crc == 22390) if (crc == 22390)
@ -6846,6 +6943,7 @@ pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc)
Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n");
} }
} }
#endif
return true; return true;
} }
@ -6961,7 +7059,7 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks
csqcprogs = InitProgs(&csqcprogparms); csqcprogs = InitProgs(&csqcprogparms);
csqc_world.progs = csqcprogs; csqc_world.progs = csqcprogs;
csqc_world.usesolidcorpse = true; csqc_world.usesolidcorpse = true;
PR_Configure(csqcprogs, pr_csqc_memsize.ival, MAX_PROGS, pr_enable_profiling.ival); PR_Configure(csqcprogs, PR_ReadBytesString(pr_csqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);
csqc_world.worldmodel = cl.worldmodel; csqc_world.worldmodel = cl.worldmodel;
csqc_world.Event_Touch = CSQC_Event_Touch; csqc_world.Event_Touch = CSQC_Event_Touch;
csqc_world.Event_Think = CSQC_Event_Think; csqc_world.Event_Think = CSQC_Event_Think;
@ -6978,7 +7076,9 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks
return false; return false;
} }
#ifndef csqc_isdarkplaces
csqc_isdarkplaces = false; csqc_isdarkplaces = false;
#endif
if (csdatenabled || csqc_singlecheats || anycsqc) if (csdatenabled || csqc_singlecheats || anycsqc)
{ {
csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat"); csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat");
@ -7057,6 +7157,8 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks
if (str) if (str)
{ {
char *s = Info_ValueForKey(cl.serverinfo, "map"); char *s = Info_ValueForKey(cl.serverinfo, "map");
if (!*s)
s = cl.model_name[1];
if (!*s) if (!*s)
s = "unknown"; s = "unknown";
*str = PR_NewString(csqcprogs, s); *str = PR_NewString(csqcprogs, s);
@ -7440,6 +7542,7 @@ qboolean CSQC_DrawView(void)
int ticlimit = 10; int ticlimit = 10;
float mintic = 0.01; float mintic = 0.01;
double clframetime = host_frametime; double clframetime = host_frametime;
RSpeedLocals();
csqc_resortfrags = true; csqc_resortfrags = true;
csqctime = Sys_DoubleTime(); csqctime = Sys_DoubleTime();
@ -7457,6 +7560,7 @@ qboolean CSQC_DrawView(void)
csqc_dp_lastwas3d = false; csqc_dp_lastwas3d = false;
RSpeedRemark();
if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1) if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1)
{ {
csqc_world.physicstime = cl.servertime; csqc_world.physicstime = cl.servertime;
@ -7490,12 +7594,12 @@ qboolean CSQC_DrawView(void)
csqc_world.physicstime += host_frametime; csqc_world.physicstime += host_frametime;
} }
} }
RSpeedEnd(RSPEED_CSQCPHYSICS);
RSpeedRemark();
host_frametime = clframetime; host_frametime = clframetime;
//always revert to a usable default.
CSQC_ChangeLocalPlayer(cl_forceseat.ival?(cl_forceseat.ival - 1) % cl.splitclients:0);
if (csqcg.frametime) if (csqcg.frametime)
{ {
if (csqc_isdarkplaces) if (csqc_isdarkplaces)
@ -7516,28 +7620,6 @@ qboolean CSQC_DrawView(void)
if (csqcg.numclientseats) if (csqcg.numclientseats)
*csqcg.numclientseats = cl.splitclients; *csqcg.numclientseats = cl.splitclients;
DropPunchAngle (csqc_playerview);
if (cl.worldmodel)
Surf_LessenStains();
if (!cl.paused)
{
if (csqcg.clientcommandframe)
*csqcg.clientcommandframe = cl.movesequence;
if (csqcg.servercommandframe)
*csqcg.servercommandframe = cl.ackedmovesequence;
if (csqcg.gamespeed)
*csqcg.gamespeed = cl.gamespeed;
}
else
{
if (csqcg.clientcommandframe)
*csqcg.clientcommandframe = cl.movesequence;
if (csqcg.servercommandframe)
*csqcg.servercommandframe = cl.ackedmovesequence;
if (csqcg.gamespeed)
*csqcg.gamespeed = 0;
}
if (csqcg.intermission) if (csqcg.intermission)
*csqcg.intermission = cl.intermissionmode; *csqcg.intermission = cl.intermissionmode;
@ -7551,7 +7633,17 @@ qboolean CSQC_DrawView(void)
*csqcg.cltime = realtime; *csqcg.cltime = realtime;
if (csqcg.simtime) if (csqcg.simtime)
*csqcg.simtime = cl.servertime; *csqcg.simtime = cl.servertime;
if (csqcg.clientcommandframe)
*csqcg.clientcommandframe = cl.movesequence;
if (csqcg.servercommandframe)
*csqcg.servercommandframe = cl.ackedmovesequence;
if (csqcg.gamespeed)
*csqcg.gamespeed = cl.gamespeed;
if (cl.paused)
{
if (csqcg.gamespeed)
*csqcg.gamespeed = 0;
}
if (cl.currentpackentities && cl.previouspackentities) if (cl.currentpackentities && cl.previouspackentities)
{ {
if (csqcg.netnewtime) if (csqcg.netnewtime)
@ -7562,25 +7654,32 @@ qboolean CSQC_DrawView(void)
*csqcg.netdeltatime = cl.currentpackentities->servertime - cl.previouspackentities->servertime; *csqcg.netdeltatime = cl.currentpackentities->servertime - cl.previouspackentities->servertime;
} }
//always revert to a usable default.
CSQC_ChangeLocalPlayer(cl_forceseat.ival?(cl_forceseat.ival - 1) % cl.splitclients:0);
DropPunchAngle (csqc_playerview); //FIXME: this seems like the wrong place for this.
if (cl.worldmodel)
Surf_LessenStains();
CSQC_RunThreads(); //wake up any qc threads CSQC_RunThreads(); //wake up any qc threads
#ifndef NOLEGACY
if (csqcg.autocvar_vid_conwidth) if (csqcg.autocvar_vid_conwidth)
*csqcg.autocvar_vid_conwidth = vid.width; *csqcg.autocvar_vid_conwidth = vid.width;
if (csqcg.autocvar_vid_conheight) if (csqcg.autocvar_vid_conheight)
*csqcg.autocvar_vid_conheight = vid.height; *csqcg.autocvar_vid_conheight = vid.height;
#endif
//EXT_CSQC_1
{ {
void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); void *pr_globals = PR_globals(csqcprogs, PR_CURRENT);
G_FLOAT(OFS_PARM0) = vid.width; G_FLOAT(OFS_PARM0) = vid.width;
G_FLOAT(OFS_PARM1) = vid.height; G_FLOAT(OFS_PARM1) = vid.height;
G_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_emenu) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1]; G_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_emenu) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1];
if (csqcg.f_updateviewloading && cls.state && cls.state < ca_active)
PR_ExecuteProgram(csqcprogs, csqcg.f_updateviewloading);
else
PR_ExecuteProgram(csqcprogs, csqcg.f_updateview);
} }
//end EXT_CSQC_1
if (csqcg.f_updateviewloading && cls.state && cls.state < ca_active)
PR_ExecuteProgram(csqcprogs, csqcg.f_updateviewloading);
else
PR_ExecuteProgram(csqcprogs, csqcg.f_updateview);
if (*r_refdef.rt_destcolour[0].texname) if (*r_refdef.rt_destcolour[0].texname)
{ {
@ -7590,6 +7689,8 @@ qboolean CSQC_DrawView(void)
BE_RenderToTextureUpdate2d(true); BE_RenderToTextureUpdate2d(true);
} }
RSpeedEnd(RSPEED_CSQCREDRAW);
return true; return true;
} }
@ -8111,10 +8212,13 @@ void CSQC_ParseEntities(void)
if (csqcg.netdeltatime) if (csqcg.netdeltatime)
*csqcg.netdeltatime = cl.gametime - cl.oldgametime; *csqcg.netdeltatime = cl.gametime - cl.oldgametime;
if (csqcg.clientcommandframe) if (!csqc_isdarkplaces)
*csqcg.clientcommandframe = cl.movesequence; {
if (csqcg.servercommandframe) if (csqcg.clientcommandframe)
*csqcg.servercommandframe = cl.ackedmovesequence; *csqcg.clientcommandframe = cl.movesequence;
if (csqcg.servercommandframe)
*csqcg.servercommandframe = cl.ackedmovesequence;
}
for(;;) for(;;)
{ {
@ -8175,9 +8279,20 @@ void CSQC_ParseEntities(void)
ent = csqcent[entnum]; ent = csqcent[entnum];
if (!ent) if (!ent)
{ {
ent = (csqcedict_t*)ED_Alloc(csqcprogs, false, 0); if (csqcg.ent_spawn)
csqcent[entnum] = ent; {
ent->xv->entnum = entnum; *csqcg.self = 0;
G_FLOAT(OFS_PARM0) = entnum;
PR_ExecuteProgram(csqcprogs, csqcg.ent_spawn);
ent = csqcent[entnum] = (csqcedict_t*)PROG_TO_WEDICT(csqcprogs, *csqcg.self); //allow the mod to change the ent.
}
else
{
ent = (csqcedict_t*)ED_Alloc(csqcprogs, false, 0);
csqcent[entnum] = ent;
ent->xv->entnum = entnum;
}
G_FLOAT(OFS_PARM0) = true; G_FLOAT(OFS_PARM0) = true;
if (cl_csqcdebug.ival) if (cl_csqcdebug.ival)
@ -8194,6 +8309,8 @@ void CSQC_ParseEntities(void)
csqc_mayread = true; csqc_mayread = true;
PR_ExecuteProgram(csqcprogs, csqcg.ent_update); PR_ExecuteProgram(csqcprogs, csqcg.ent_update);
csqc_mayread = false; csqc_mayread = false;
if (csqcg.ent_spawn)
csqcent[entnum] = (csqcedict_t*)PROG_TO_WEDICT(csqcprogs, *csqcg.self); //allow the mod to change the ent.
if (cl.csqcdebug) if (cl.csqcdebug)
{ {

View file

@ -366,7 +366,19 @@ void CL_LoadFont_f(void)
while(sizenum < Cmd_Argc()) while(sizenum < Cmd_Argc())
{ {
int sz = atoi(Cmd_Argv(sizenum++)); const char *a = Cmd_Argv(sizenum++);
int sz;
if (!strcmp(a, "scale"))
{
sizenum++;
continue;
}
if (!strcmp(a, "voffset"))
{
sizenum++;
continue;
}
sz = atoi(a);
if (sz <= 0) if (sz <= 0)
sz = 8; sz = 8;
@ -388,7 +400,8 @@ void CL_LoadFont_f(void)
} }
} }
if (dpcompat_console.ival) //FIXME: slotnum0==default is problematic.
if (dpcompat_console.ival && (slotnum == 1 || (slotnum == 0 && !*gl_font.string)))
Cvar_Set(&gl_font, facename); Cvar_Set(&gl_font, facename);
} }
} }

View file

@ -923,9 +923,8 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int
if (normal) if (normal)
VectorCopy (trace.plane.normal, normal); VectorCopy (trace.plane.normal, normal);
VectorAdd (pe->origin, trace.endpos, impact); VectorAdd (pe->origin, trace.endpos, impact);
result = pe->info;
} }
result = pe->info;
} }
if (trace.startsolid) if (trace.startsolid)
{ {

View file

@ -4446,25 +4446,25 @@ char *particle_set_q2part =
"{ //FIXME\n" "{ //FIXME\n"
"assoc placeholder\n" "assoc placeholder\n"
"}\n" "}\n"
"/*\n"
"r_part teq2_heatbeam_steam\n" //r_part teq2_heatbeam_steam
"{\n" //{
"count 20\n" // count 20
"colorindex 0xe0 7\n" // colorindex 0xe0 7
// magnitude 60 //// magnitude 60
"texture \"classicparticle\"\n" // texture "classicparticle"
"tcoords 0 0 16 16 32\n" // tcoords 0 0 16 16 32
"scale 1\n" // scale 1
"alpha 1\n" // alpha 1
"die 0.3 0.8\n" // die 0.3 0.8
"randomvel 20 magnitude/3\n" // randomvel 20 magnitude/3
"veladd magnitude\n" // veladd magnitude
"orgadd magnitude/10\n" // orgadd magnitude/10
"spawnorg 4\n" // spawnorg 4
"gravity -400\n" // gravity -400
"scalefactor 0.8\n" // scalefactor 0.8
"}\n" //}
"*/\n"
//this is apparently just a trail effect (palette index specified by netcode) //this is apparently just a trail effect (palette index specified by netcode)
"r_part teq2_forcewall\n" "r_part teq2_forcewall\n"

View file

@ -3371,7 +3371,7 @@ int Surf_NewExternalLightmaps(int count, char *filepattern, qboolean deluxe)
lightmap[i]->modified = false; lightmap[i]->modified = false;
lightmap[i]->external = true; lightmap[i]->external = true;
lightmap[i]->hasdeluxe = (deluxe && ((i - numlightmaps)&1)); lightmap[i]->hasdeluxe = (deluxe && !((i - numlightmaps)&1));
Q_snprintfz(nname, sizeof(nname), filepattern, i - numlightmaps); Q_snprintfz(nname, sizeof(nname), filepattern, i - numlightmaps);
@ -3685,6 +3685,12 @@ void Surf_NewMap (void)
r_viewcluster2 = -1; r_viewcluster2 = -1;
r_oldviewcluster2 = 0; r_oldviewcluster2 = 0;
TRACE(("dbg: Surf_NewMap: clear particles\n"));
P_ClearParticles ();
CL_RegisterParticles();
Shader_DoReload();
if (cl.worldmodel) if (cl.worldmodel)
{ {
if (cl.worldmodel->loadstate == MLS_LOADING) if (cl.worldmodel->loadstate == MLS_LOADING)
@ -3695,11 +3701,8 @@ void Surf_NewMap (void)
if (!pe) if (!pe)
Cvar_ForceCallback(&r_particlesystem); Cvar_ForceCallback(&r_particlesystem);
R_Clutter_Purge(); R_Clutter_Purge();
TRACE(("dbg: Surf_NewMap: clear particles\n"));
P_ClearParticles ();
TRACE(("dbg: Surf_NewMap: wiping them stains (getting the cloth out)\n")); TRACE(("dbg: Surf_NewMap: wiping them stains (getting the cloth out)\n"));
Surf_WipeStains(); Surf_WipeStains();
CL_RegisterParticles();
TRACE(("dbg: Surf_NewMap: building lightmaps\n")); TRACE(("dbg: Surf_NewMap: building lightmaps\n"));
Surf_BuildLightmaps (); Surf_BuildLightmaps ();
@ -3710,7 +3713,6 @@ TRACE(("dbg: Surf_NewMap: ui\n"));
#endif #endif
TRACE(("dbg: Surf_NewMap: tp\n")); TRACE(("dbg: Surf_NewMap: tp\n"));
TP_NewMap(); TP_NewMap();
R_SetSky(cl.skyname);
for (i = 0; i < cl.num_statics; i++) for (i = 0; i < cl.num_statics; i++)
{ {
@ -3751,6 +3753,8 @@ void Surf_PreNewMap(void)
r_oldviewcluster = -1; r_oldviewcluster = -1;
r_viewcluster2 = -1; r_viewcluster2 = -1;
r_oldviewcluster2 = -1; r_oldviewcluster2 = -1;
Shader_DoReload();
} }

View file

@ -370,7 +370,8 @@ extern uploadfmt_t lightmap_fmt; //bgra32, rgba32, rgb24, lum8
void QDECL Surf_RebuildLightmap_Callback (struct cvar_s *var, char *oldvalue); void QDECL Surf_RebuildLightmap_Callback (struct cvar_s *var, char *oldvalue);
void R_SetSky(char *skyname); /*override all sky shaders*/ void R_SkyShutdown(void);
void R_SetSky(const char *skyname);
#if defined(GLQUAKE) #if defined(GLQUAKE)
void GLR_Init (void); void GLR_Init (void);
@ -634,6 +635,7 @@ extern cvar_t r_lightstylesmooth;
extern cvar_t r_lightstylesmooth_limit; extern cvar_t r_lightstylesmooth_limit;
extern cvar_t r_lightstylespeed; extern cvar_t r_lightstylespeed;
extern cvar_t r_lightstylescale; extern cvar_t r_lightstylescale;
extern cvar_t r_lightmap_scale;
extern cvar_t gl_nocolors; extern cvar_t gl_nocolors;
extern cvar_t gl_load24bit; extern cvar_t gl_load24bit;
extern cvar_t gl_finish; extern cvar_t gl_finish;
@ -647,14 +649,15 @@ extern cvar_t r_meshpitch;
enum { enum {
RSPEED_TOTALREFRESH, RSPEED_TOTALREFRESH,
RSPEED_CSQCPHYSICS,
RSPEED_CSQCREDRAW,
RSPEED_LINKENTITIES, RSPEED_LINKENTITIES,
RSPEED_PROTOCOL,
RSPEED_WORLDNODE, RSPEED_WORLDNODE,
RSPEED_WORLD,
RSPEED_DRAWENTITIES,
RSPEED_STENCILSHADOWS,
RSPEED_FULLBRIGHTS,
RSPEED_DYNAMIC, RSPEED_DYNAMIC,
RSPEED_OPAQUE,
RSPEED_RTLIGHTS,
RSPEED_TRANSPARENTS,
RSPEED_PROTOCOL,
RSPEED_PARTICLES, RSPEED_PARTICLES,
RSPEED_PARTICLESDRAW, RSPEED_PARTICLESDRAW,
RSPEED_PALETTEFLASHES, RSPEED_PALETTEFLASHES,

View file

@ -47,10 +47,7 @@ void QDECL SCR_Fov_Callback (struct cvar_s *var, char *oldvalue);
void QDECL Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue); void QDECL Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue);
void QDECL R_SkyBox_Changed (struct cvar_s *var, char *oldvalue) void QDECL R_SkyBox_Changed (struct cvar_s *var, char *oldvalue)
{ {
if (qrenderer != QR_NONE && cl.worldmodel) Shader_NeedReload(false);
{
R_SetSky(cl.skyname);
}
} }
void R_ForceSky_f(void) void R_ForceSky_f(void)
{ {
@ -58,7 +55,7 @@ void R_ForceSky_f(void)
{ {
extern cvar_t r_skyboxname; extern cvar_t r_skyboxname;
if (*r_skyboxname.string) if (*r_skyboxname.string)
Con_Printf("Current user skybox is %s\n", cl.skyname); Con_Printf("Current user skybox is %s\n", r_skyboxname.string);
else if (*cl.skyname) else if (*cl.skyname)
Con_Printf("Current per-map skybox is %s\n", cl.skyname); Con_Printf("Current per-map skybox is %s\n", cl.skyname);
else else
@ -66,8 +63,7 @@ void R_ForceSky_f(void)
} }
else else
{ {
Q_strncpyz(cl.skyname, Cmd_Argv(1), sizeof(cl.skyname)); R_SetSky(Cmd_Argv(1));
R_SetSky(cl.skyname);
} }
} }
@ -98,6 +94,7 @@ static cvar_t gl_driver = CVARF ("gl_driver", "",
cvar_t gl_shadeq1_name = CVARD ("gl_shadeq1_name", "*", "Rename all surfaces from quake1 bsps using this pattern for the purposes of shader names."); cvar_t gl_shadeq1_name = CVARD ("gl_shadeq1_name", "*", "Rename all surfaces from quake1 bsps using this pattern for the purposes of shader names.");
extern cvar_t r_vertexlight; extern cvar_t r_vertexlight;
extern cvar_t r_forceprogramify; extern cvar_t r_forceprogramify;
extern cvar_t dpcompat_nopremulpics;
cvar_t mod_md3flags = CVARD ("mod_md3flags", "1", "The flags field of md3s was never officially defined. If this is set to 1, the flags will be treated identically to mdl files. Otherwise they will be ignored. Naturally, this is required to provide rotating pickups in quake."); cvar_t mod_md3flags = CVARD ("mod_md3flags", "1", "The flags field of md3s was never officially defined. If this is set to 1, the flags will be treated identically to mdl files. Otherwise they will be ignored. Naturally, this is required to provide rotating pickups in quake.");
@ -126,7 +123,7 @@ cvar_t r_dynamic = CVARFD ("r_dynamic", IFMINIMAL("0","1"),
cvar_t r_fastturb = CVARF ("r_fastturb", "0", cvar_t r_fastturb = CVARF ("r_fastturb", "0",
CVAR_SHADERSYSTEM); CVAR_SHADERSYSTEM);
cvar_t r_fastsky = CVARF ("r_fastsky", "0", cvar_t r_fastsky = CVARF ("r_fastsky", "0",
CVAR_ARCHIVE | CVAR_SHADERSYSTEM); CVAR_ARCHIVE);
cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0", cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0",
CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM); CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);
cvar_t r_fb_bmodels = CVARAF("r_fb_bmodels", "1", cvar_t r_fb_bmodels = CVARAF("r_fb_bmodels", "1",
@ -159,6 +156,7 @@ cvar_t r_lightstylesmooth = CVARF ("r_lightstylesmooth", "0", CVAR_ARCHIVE);
cvar_t r_lightstylesmooth_limit = CVAR ("r_lightstylesmooth_limit", "2"); cvar_t r_lightstylesmooth_limit = CVAR ("r_lightstylesmooth_limit", "2");
cvar_t r_lightstylespeed = CVAR ("r_lightstylespeed", "10"); cvar_t r_lightstylespeed = CVAR ("r_lightstylespeed", "10");
cvar_t r_lightstylescale = CVAR ("r_lightstylescale", "1"); cvar_t r_lightstylescale = CVAR ("r_lightstylescale", "1");
cvar_t r_lightmap_scale = CVARFD ("r_shadow_realtime_nonworld_lightmaps", "1", 0, "Scaler for lightmaps used when not using realtime world lighting. Probably broken.");
cvar_t r_hdr_irisadaptation = CVARF ("r_hdr_irisadaptation", "0", CVAR_ARCHIVE); cvar_t r_hdr_irisadaptation = CVARF ("r_hdr_irisadaptation", "0", CVAR_ARCHIVE);
cvar_t r_hdr_irisadaptation_multiplier = CVAR ("r_hdr_irisadaptation_multiplier", "2"); cvar_t r_hdr_irisadaptation_multiplier = CVAR ("r_hdr_irisadaptation_multiplier", "2");
cvar_t r_hdr_irisadaptation_minvalue = CVAR ("r_hdr_irisadaptation_minvalue", "0.5"); cvar_t r_hdr_irisadaptation_minvalue = CVAR ("r_hdr_irisadaptation_minvalue", "0.5");
@ -398,6 +396,7 @@ cvar_t gl_mipcap = CVARAFC("d_mipcap", "0 1000", "gl_miptexLevel",
cvar_t gl_texturemode2d = CVARFCD("gl_texturemode2d", "GL_LINEAR", cvar_t gl_texturemode2d = CVARFCD("gl_texturemode2d", "GL_LINEAR",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, Image_TextureMode_Callback, CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, Image_TextureMode_Callback,
"Specifies how 2d images are sampled. format is a 3-tupple "); "Specifies how 2d images are sampled. format is a 3-tupple ");
cvar_t r_font_linear = CVARF("r_font_linear", "1", CVAR_RENDERERLATCH);
cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffer", CVAR_ARCHIVE, "Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten."); cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffer", CVAR_ARCHIVE, "Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten.");
@ -435,7 +434,7 @@ cvar_t r_vertexdlights = CVARD ("r_vertexdlights", "0", "Determine model li
cvar_t vid_preservegamma = CVARD ("vid_preservegamma", "0", "Restore initial hardware gamma ramps when quitting."); cvar_t vid_preservegamma = CVARD ("vid_preservegamma", "0", "Restore initial hardware gamma ramps when quitting.");
cvar_t vid_hardwaregamma = CVARFD ("vid_hardwaregamma", "1", cvar_t vid_hardwaregamma = CVARFD ("vid_hardwaregamma", "1",
CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Use hardware gamma ramps. 0=loadtime-gamma, 1=glsl(windowed) or hardware(fullscreen), 2=always glsl, 3=always hardware gamma."); CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Use hardware gamma ramps. 0=ugly texture-based gamma, 1=glsl(windowed) or hardware(fullscreen), 2=always glsl, 3=always hardware gamma (disabled if hardware doesn't support).");
cvar_t vid_desktopgamma = CVARFD ("vid_desktopgamma", "0", cvar_t vid_desktopgamma = CVARFD ("vid_desktopgamma", "0",
CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Apply gamma ramps upon the desktop rather than the window."); CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Apply gamma ramps upon the desktop rather than the window.");
@ -816,6 +815,7 @@ void Renderer_Init(void)
Cvar_Register(&r_lightstylesmooth_limit, GRAPHICALNICETIES); Cvar_Register(&r_lightstylesmooth_limit, GRAPHICALNICETIES);
Cvar_Register(&r_lightstylespeed, GRAPHICALNICETIES); Cvar_Register(&r_lightstylespeed, GRAPHICALNICETIES);
Cvar_Register(&r_lightstylescale, GRAPHICALNICETIES); Cvar_Register(&r_lightstylescale, GRAPHICALNICETIES);
Cvar_Register(&r_lightmap_scale, GRAPHICALNICETIES);
Cvar_Register(&r_hdr_irisadaptation, GRAPHICALNICETIES); Cvar_Register(&r_hdr_irisadaptation, GRAPHICALNICETIES);
Cvar_Register(&r_hdr_irisadaptation_multiplier, GRAPHICALNICETIES); Cvar_Register(&r_hdr_irisadaptation_multiplier, GRAPHICALNICETIES);
@ -936,6 +936,7 @@ void Renderer_Init(void)
Cvar_Register (&gl_miptexLevel, GRAPHICALNICETIES); Cvar_Register (&gl_miptexLevel, GRAPHICALNICETIES);
Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS); Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS);
Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS); Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS);
Cvar_Register (&r_font_linear, GLRENDEREROPTIONS);
Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS);
Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS); Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS);
Cvar_Register (&r_max_gpu_bones, GRAPHICALNICETIES); Cvar_Register (&r_max_gpu_bones, GRAPHICALNICETIES);
@ -965,6 +966,7 @@ void Renderer_Init(void)
Cvar_Register (&r_polygonoffset_stencil_offset, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_stencil_offset, GLRENDEREROPTIONS);
Cvar_Register (&r_forceprogramify, GLRENDEREROPTIONS); Cvar_Register (&r_forceprogramify, GLRENDEREROPTIONS);
Cvar_Register (&dpcompat_nopremulpics, GLRENDEREROPTIONS);
#ifdef VKQUAKE #ifdef VKQUAKE
Cvar_Register (&vk_stagingbuffers, VKRENDEREROPTIONS); Cvar_Register (&vk_stagingbuffers, VKRENDEREROPTIONS);
Cvar_Register (&vk_submissionthread, VKRENDEREROPTIONS); Cvar_Register (&vk_submissionthread, VKRENDEREROPTIONS);
@ -1474,10 +1476,6 @@ TRACE(("dbg: R_ApplyRenderer: initing mods\n"));
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (sv.world.worldmodel) if (sv.world.worldmodel)
{ {
#ifdef Q2SERVER
q2edict_t *q2ent;
#endif
TRACE(("dbg: R_ApplyRenderer: reloading server map\n")); TRACE(("dbg: R_ApplyRenderer: reloading server map\n"));
sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_WARNSYNC); sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_WARNSYNC);
TRACE(("dbg: R_ApplyRenderer: loaded\n")); TRACE(("dbg: R_ApplyRenderer: loaded\n"));
@ -1507,6 +1505,7 @@ TRACE(("dbg: R_ApplyRenderer: clearing world\n"));
#ifdef Q2SERVER #ifdef Q2SERVER
else if (svs.gametype == GT_QUAKE2) else if (svs.gametype == GT_QUAKE2)
{ {
q2edict_t *q2ent;
for (i = 0; i < Q2MAX_MODELS; i++) for (i = 0; i < Q2MAX_MODELS; i++)
{ {
if (sv.strings.configstring[Q2CS_MODELS+i] && *sv.strings.configstring[Q2CS_MODELS+i] && (!strcmp(sv.strings.configstring[Q2CS_MODELS+i] + strlen(sv.strings.configstring[Q2CS_MODELS+i]) - 4, ".bsp") || i-1 < sv.world.worldmodel->numsubmodels)) if (sv.strings.configstring[Q2CS_MODELS+i] && *sv.strings.configstring[Q2CS_MODELS+i] && (!strcmp(sv.strings.configstring[Q2CS_MODELS+i] + strlen(sv.strings.configstring[Q2CS_MODELS+i]) - 4, ".bsp") || i-1 < sv.world.worldmodel->numsubmodels))

View file

@ -4239,11 +4239,39 @@ void *WIN_CreateCursor(const char *filename, float hotx, float hoty, float scale
if (!filedata) if (!filedata)
return NULL; return NULL;
hasalpha = false;
rgbadata_start = Read32BitImageFile(filedata, filelen, &width, &height, &hasalpha, "cursor"); rgbadata_start = Read32BitImageFile(filedata, filelen, &width, &height, &hasalpha, "cursor");
FS_FreeFile(filedata); FS_FreeFile(filedata);
if (!rgbadata_start) if (!rgbadata_start)
return NULL; return NULL;
if (!hasalpha && !strchr(filename, ':'))
{ //people seem to insist on using jpgs, which don't have alpha.
//screw over the alpha channel if needed.
unsigned int alpha_width, alpha_height, p;
char aname[MAX_QPATH];
unsigned char *alphadata;
char *alph;
size_t alphsize;
char ext[8];
COM_StripExtension(filename, aname, sizeof(aname));
COM_FileExtension(filename, ext, sizeof(ext));
Q_strncatz(aname, "_alpha.", sizeof(aname));
Q_strncatz(aname, ext, sizeof(aname));
alphsize = FS_LoadFile(filename, &alph);
if (alph)
{
if ((alphadata = Read32BitImageFile(alph, alphsize, &alpha_width, &alpha_height, &hasalpha, aname)))
{
if (alpha_width == width && alpha_height == height)
for (p = 0; p < alpha_width*alpha_height; p++)
rgbadata_start[(p<<2) + 3] = (alphadata[(p<<2) + 0] + alphadata[(p<<2) + 1] + alphadata[(p<<2) + 2])/3;
BZ_Free(alphadata);
}
FS_FreeFile(alph);
}
}
if (scale != 1) if (scale != 1)
{ {
int nw,nh; int nw,nh;

View file

@ -628,8 +628,10 @@ qboolean Con_Editor_Key(console_t *con, unsigned int unicode, int key)
fname += 4; fname += 4;
else if (!strncmp(fname, "source/", 7)) else if (!strncmp(fname, "source/", 7))
fname += 7; fname += 7;
else if (!strncmp(fname, "qcsrc/", 7)) else if (!strncmp(fname, "qcsrc/", 6))
fname += 7; fname += 6;
else if (!strncmp(fname, "progs/", 6))
fname += 6;
cl = con->userline; cl = con->userline;
@ -878,6 +880,9 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st
return DEBUG_TRACE_OFF; //get lost return DEBUG_TRACE_OFF; //get lost
} }
if (!strncmp(filename, "./", 2))
filename+=2;
//we can cope with no line info by displaying asm //we can cope with no line info by displaying asm
if (editormodal || !statement if (editormodal || !statement
|| !line || *line == -1 //FIXME || !line || *line == -1 //FIXME
@ -960,10 +965,10 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st
{ {
if (fatal) if (fatal)
{ {
Con_Printf(CON_ERROR "Unable to find %s\n", filename); Con_Printf(CON_ERROR "Unable to find file \"%s\"\n", filename);
return DEBUG_TRACE_ABORT; return DEBUG_TRACE_ABORT;
} }
Con_Printf(CON_WARNING "Unable to find %s\n", filename); Con_Printf(CON_WARNING "Unable to find file \"%s\"\n", filename);
return DEBUG_TRACE_OFF; //whoops return DEBUG_TRACE_OFF; //whoops
} }
} }

View file

@ -1948,8 +1948,9 @@ void R_DrawNameTags(void)
score = DotProduct(diff, vpn);// r_refdef.viewaxis[0]); score = DotProduct(diff, vpn);// r_refdef.viewaxis[0]);
if (score > bestscore) if (score > bestscore)
{ {
int hitent;
vec3_t imp; vec3_t imp;
if (CL_TraceLine(r_refdef.vieworg, org, imp, NULL, NULL)>=1) if (CL_TraceLine(r_refdef.vieworg, org, imp, NULL, &hitent)>=1 || hitent == i)
{ {
best = i; best = i;
bestscore = score; bestscore = score;

View file

@ -799,6 +799,7 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
{ {
char token[4096]; char token[4096];
char key[128]; char key[128];
char skyname[128];
const char *data = Mod_GetEntitiesString(wmodel); const char *data = Mod_GetEntitiesString(wmodel);
#ifdef PACKAGE_TEXWAD #ifdef PACKAGE_TEXWAD
mapskys_t *msky; mapskys_t *msky;
@ -815,10 +816,10 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
#endif #endif
// this hack is necessary to ensure Quake 2 maps get their default skybox, without breaking q1 etc // this hack is necessary to ensure Quake 2 maps get their default skybox, without breaking q1 etc
if (wmodel->fromgame == fg_quake2) if (wmodel && wmodel->fromgame == fg_quake2)
strcpy(cl.skyname, "unit1_"); strcpy(skyname, "unit1_");
else else
cl.skyname[0] = '\0'; skyname[0] = '\0';
if (data) if (data)
if ((data=COM_ParseOut(data, token, sizeof(token)))) //read the map info. if ((data=COM_ParseOut(data, token, sizeof(token)))) //read the map info.
@ -887,11 +888,11 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
} }
else if (!strcmp("skyname", key)) // for HalfLife maps else if (!strcmp("skyname", key)) // for HalfLife maps
{ {
Q_strncpyz(cl.skyname, token, sizeof(cl.skyname)); Q_strncpyz(skyname, token, sizeof(skyname));
} }
else if (!strcmp("sky", key)) // for Quake2 maps else if (!strcmp("sky", key)) // for Quake2 maps
{ {
Q_strncpyz(cl.skyname, token, sizeof(cl.skyname)); Q_strncpyz(skyname, token, sizeof(skyname));
} }
else if (!strcmp("skyrotate", key)) //q2 feature else if (!strcmp("skyrotate", key)) //q2 feature
{ {
@ -917,17 +918,22 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
} }
} }
COM_FileBase (wmodel->name, token, sizeof(token)); if (wmodel)
{
COM_FileBase (wmodel->name, token, sizeof(token));
#ifdef PACKAGE_TEXWAD #ifdef PACKAGE_TEXWAD
//map-specific sky override feature //map-specific sky override feature
for (msky = mapskies; msky; msky = msky->next) for (msky = mapskies; msky; msky = msky->next)
{
if (!strcmp(msky->mapname, token))
{ {
Q_strncpyz(cl.skyname, msky->skyname, sizeof(cl.skyname)); if (!strcmp(msky->mapname, token))
break; {
Q_strncpyz(skyname, msky->skyname, sizeof(skyname));
break;
}
} }
}
#endif #endif
}
R_SetSky(skyname);
} }

View file

@ -195,7 +195,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PSET_SCRIPT #define PSET_SCRIPT
// #define PLUGINS //qvm/dll plugins. // #define PLUGINS //qvm/dll plugins.
// #define SUPPORT_ICE //Interactive Connectivity Establishment protocol, for peer-to-peer connections // #define SUPPORT_ICE //Interactive Connectivity Establishment protocol, for peer-to-peer connections
#define CSQC_DAT //support for csqc #define CSQC_DAT //support for csqc
// #define VOICECHAT // #define VOICECHAT
#undef AVAIL_JPEGLIB #undef AVAIL_JPEGLIB
@ -261,6 +261,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define INTERQUAKEMODELS #define INTERQUAKEMODELS
#define RAGDOLL #define RAGDOLL
#define USEAREAGRID //world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.
#define HUFFNETWORK //huffman network compression #define HUFFNETWORK //huffman network compression
// #define PACKAGE_DOOMWAD //doom wad support (maps+sprites are separate) // #define PACKAGE_DOOMWAD //doom wad support (maps+sprites are separate)
// #define MAP_DOOM //doom map support // #define MAP_DOOM //doom map support
@ -312,7 +313,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PSET_SCRIPT #define PSET_SCRIPT
#define PSET_CLASSIC #define PSET_CLASSIC
//#define PSET_DARKPLACES
#define HAVE_CDPLAYER //includes cd playback. actual cds. faketracks are supported regardless. #define HAVE_CDPLAYER //includes cd playback. actual cds. faketracks are supported regardless.
@ -378,7 +378,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define DISTRIBUTIONLONG "Forethought Entertainment" //effectively the 'company' name #define DISTRIBUTIONLONG "Forethought Entertainment" //effectively the 'company' name
#endif #endif
#ifndef FULLENGINENAME #ifndef FULLENGINENAME
#define FULLENGINENAME "FTE QuakeWorld" //the posh name for the engine #define FULLENGINENAME "FTE Quake" //the posh name for the engine
#endif #endif
#ifndef ENGINEWEBSITE #ifndef ENGINEWEBSITE
#define ENGINEWEBSITE "http://fte.triptohell.info" //url for program #define ENGINEWEBSITE "http://fte.triptohell.info" //url for program

View file

@ -47,7 +47,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//#define MAX_MAP_LIGHTING 0x100000 //#define MAX_MAP_LIGHTING 0x100000
//#define MAX_MAP_VISIBILITY 0x200000 //#define MAX_MAP_VISIBILITY 0x200000
#define SANITY_MAX_MAP_BRUSHSIDES 0x100000 #define SANITY_MAX_MAP_BRUSHSIDES ((~0u)/sizeof(q2cbrushside_t))
// key / value pair sizes // key / value pair sizes
@ -371,13 +371,13 @@ typedef struct q2miptex_s
// 16 bit short limits // 16 bit short limits
#define SANITY_MAX_Q2MAP_MODELS 1024 #define SANITY_MAX_Q2MAP_MODELS 1024
//#define MAX_Q2MAP_ENTITIES 2048 //#define MAX_Q2MAP_ENTITIES 2048
#define SANITY_MAX_MAP_BRUSHES 0x10000 #define SANITY_MAX_MAP_BRUSHES (~0u/sizeof(*out))
#define SANITY_MAX_MAP_LEAFFACES 262144 //sanity only
#define MAX_Q2MAP_AREAS 256 #define MAX_Q2MAP_AREAS 256
#define MAX_Q2MAP_AREAPORTALS 1024 #define MAX_Q2MAP_AREAPORTALS 1024
//#define MAX_Q2MAP_VERTS MAX_MAP_VERTS //#define MAX_Q2MAP_VERTS MAX_MAP_VERTS
//#define MAX_Q2MAP_FACES MAX_MAP_FACES //#define MAX_Q2MAP_FACES MAX_MAP_FACES
#define SANITY_MAX_MAP_LEAFFACES 262144 //sanity only
#ifdef FTE_TARGET_WEB #ifdef FTE_TARGET_WEB
#define MAX_Q2MAP_LEAFBRUSHES (32768) //used in an array #define MAX_Q2MAP_LEAFBRUSHES (32768) //used in an array
#else #else

View file

@ -1370,68 +1370,51 @@ const char *Cmd_ExpandCvar(char *cvarterm, int maxaccesslevel, int *newaccesslev
int quotetype = 0; int quotetype = 0;
const char *cvarname; const char *cvarname;
cvarname = cvarterm; //set foo ba"r; ${foo q} -> ba\"r
if (*cvarterm == '{') //set foo bar; ${foo asis} -> ba"r
{ //set foo ba"r; ${foo q} -> ba\"r //${bar q} -> <EMPTY>
//set foo bar; ${foo asis} -> ba"r //${bar ?} -> ""
//${bar q} -> <EMPTY> //${bar !} -> <ERROR>
//${bar ?} -> "" fixup = cvarterm+strlen(cvarterm);
//${bar !} -> <ERROR> fixval = 0;
fixup = &cvarterm[strlen(cvarterm)-1]; termlen = fixup - cvarterm;
if (*fixup != '}') if (fixup-cvarterm > 2 && !strncmp(fixup-2, " ?", 2))
return NULL; { //force expansion, even if not defined.
cvarterm++; pl = 2;
fixval = *fixup; quotetype = 2;
*fixup = 0; }
termlen = fixup - cvarname; else if (fixup-cvarterm > 2 && !strncmp(fixup-2, " !", 2))
if (fixval) { //abort is not defined
termlen++; pl = 2;
if (fixup-cvarterm > 2 && !strncmp(fixup-2, " ?", 2)) quotetype = 3;
{ //force expansion, even if not defined. }
pl = 2; else if (fixup-cvarterm > 2 && !strncmp(fixup-2, " q", 2))
quotetype = 2; { //escaping it if not empty, otherwise empty.
} pl = 2;
else if (fixup-cvarterm > 2 && !strncmp(fixup-2, " !", 2)) quotetype = 1;
{ //abort is not defined }
pl = 2; else if (fixup-cvarterm > 2 && !strncmp(fixup-5, " asis", 5))
quotetype = 3; { //no escaping...
} pl = 5;
else if (fixup-cvarterm > 2 && !strncmp(fixup-2, " q", 2)) quotetype = 0;
{ //escaping it if not empty, otherwise empty.
pl = 2;
quotetype = 1;
}
else if (fixup-cvarterm > 2 && !strncmp(fixup-5, " asis", 5))
{ //no escaping...
pl = 5;
quotetype = 0;
}
else
{
pl = 0;
quotetype = 1; //default to escaping.
}
if (pl)
{
*fixup = fixval;
fixup -= pl;
fixval = *fixup;
*fixup = 0;
}
if (*cvarterm == '$')
cvarname = Cmd_ExpandCvar(cvarterm+1, maxaccesslevel, newaccesslevel, &pl);
else
cvarname = cvarterm;
} }
else else
{ {
fixup = &cvarterm[strlen(cvarterm)]; pl = 0;
fixval = *fixup; quotetype = dpcompat_console.ival; //default to escaping.
termlen = fixup - cvarname;
if (fixval)
termlen++;
} }
if (pl)
{
fixup -= pl;
fixval = *fixup;
*fixup = 0;
}
else
fixup = NULL;
if (*cvarterm == '$')
cvarname = Cmd_ExpandCvar(cvarterm+1, maxaccesslevel, newaccesslevel, &pl);
else
cvarname = cvarterm;
result = strtoul(cvarname, &t, 10); result = strtoul(cvarname, &t, 10);
if ((dpcompat_console.ival||fixval) && (*t == 0 || (*t == '-' && t[1] == 0))) //only expand $0 if its actually ${0} - this avoids conflicting with the $0 macro if ((dpcompat_console.ival||fixval) && (*t == 0 || (*t == '-' && t[1] == 0))) //only expand $0 if its actually ${0} - this avoids conflicting with the $0 macro
@ -1470,7 +1453,9 @@ const char *Cmd_ExpandCvar(char *cvarterm, int maxaccesslevel, int *newaccesslev
*newaccesslevel = 0; *newaccesslevel = 0;
} }
} }
*fixup = fixval;
if (fixup)
*fixup = fixval;
if (quotetype == 3) if (quotetype == 3)
{ {
@ -1524,46 +1509,77 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
if (c == '$' && (!(quotes&1) || dpcompat_console.ival)) if (c == '$' && (!(quotes&1) || dpcompat_console.ival))
{ {
data++; data++;
if (*data == '$')
striptrailing = (*data == '-')?true:false; { //double-dollar expands to a single dollar.
data++;
// Copy the text after '$' to a temp buffer str = "$";
i = 0; striptrailing = false;
buf[0] = 0; name_length = 0;
buf[1] = 0; buf[0] = 0;
bestvar = NULL; buf[1] = 0;
var_length = 0; }
while((c = *data)) else if (*data == '{')
{ { //${foo} can do some especially weird expansions.
if (c < ' ') data++;
break; i = 0;
if (c == ' ' && buf[0] != '{') buf[i++] = '{';
break; striptrailing = (*data == '-')?true:false;
if (c == '$' && i == 0) while (*data && *data != '}')
{
if (i < sizeof(buf)-2)
buf[i++] = *data;
data++;
}
buf[i] = 0;
bestvar = NULL;
if (expandcvars && (str = Cmd_ExpandCvar(buf+1+striptrailing, maxaccesslevel, accesslevel, &var_length)))
bestvar = str;
if (expandmacros && (str = TP_MacroString (buf+1+striptrailing, accesslevel, &var_length)))
bestvar = str;
str = bestvar;
if (*data == '}')
{ {
data++; data++;
name_length = 0; buf[i++] = '}';
str = "$"; buf[i] = 0;
break;
} }
data++; name_length = i;
buf[i++] = c;
buf[i] = 0;
if (expandcvars && (str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, accesslevel, &var_length)))
bestvar = str;
if (expandmacros && (str = TP_MacroString (buf+striptrailing, accesslevel, &var_length)))
bestvar = str;
}
if (bestvar)
{
str = bestvar;
name_length = var_length;
} }
else else
{ {
str = NULL; striptrailing = (*data == '-')?true:false;
name_length = 0;
// Copy the text after '$' to a temp buffer
i = 0;
buf[0] = 0;
buf[1] = 0;
bestvar = NULL;
var_length = 0;
while((c = *data))
{
if (c < ' ' || c == '$')
break;
if (c == ' ' && buf[0] != '{')
break;
data++;
buf[i++] = c;
buf[i] = 0;
if (expandcvars && (str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, accesslevel, &var_length)))
bestvar = str;
if (expandmacros && (str = TP_MacroString (buf+striptrailing, accesslevel, &var_length)))
bestvar = str;
}
if (bestvar)
{
str = bestvar;
name_length = var_length;
}
else
{
str = NULL;
name_length = 0;
}
} }
if (str) if (str)
@ -2406,7 +2422,7 @@ void Cmd_ForwardToServer_f (void)
return; return;
} }
#endif #endif
if (Q_strcasecmp(Cmd_Argv(1), "pext") == 0 && (cls.protocol != CP_NETQUAKE || cls.protocol_nq != CPNQ_ID || cls.proquake_angles_hack || cls.netchan.remote_address.type != NA_LOOPBACK)) if (Q_strcasecmp(Cmd_Argv(1), "pext") == 0 && (cls.protocol != CP_NETQUAKE || cls.fteprotocolextensions2 || cls.protocol_nq != CPNQ_ID || cls.proquake_angles_hack || cls.netchan.remote_address.type != NA_LOOPBACK))
{ //don't send any extension flags this if we're using cl_loopbackprotocol nqid, purely for a compat test. { //don't send any extension flags this if we're using cl_loopbackprotocol nqid, purely for a compat test.
//if you want to record compat-demos, disable extensions instead. //if you want to record compat-demos, disable extensions instead.
unsigned int fp1 = Net_PextMask(1, cls.protocol == CP_NETQUAKE), fp2 = Net_PextMask(2, cls.protocol == CP_NETQUAKE); unsigned int fp1 = Net_PextMask(1, cls.protocol == CP_NETQUAKE), fp2 = Net_PextMask(2, cls.protocol == CP_NETQUAKE);
@ -2466,7 +2482,7 @@ void Cmd_ExecuteString (const char *text, int level)
; //certain commands don't get pre-expanded in dp. evil hack. quote them to pre-expand anyway. double evil. ; //certain commands don't get pre-expanded in dp. evil hack. quote them to pre-expand anyway. double evil.
else else
text = Cmd_ExpandString(text, dest, sizeof(dest), &level, !Cmd_IsInsecure()?true:false, true); text = Cmd_ExpandString(text, dest, sizeof(dest), &level, !Cmd_IsInsecure()?true:false, true);
Cmd_TokenizeString (text, level == RESTRICT_LOCAL?true:false, false); Cmd_TokenizeString (text, (level == RESTRICT_LOCAL&&!dpcompat_console.ival)?true:false, false);
// execute the command line // execute the command line
if (!Cmd_Argc()) if (!Cmd_Argc())
@ -3898,6 +3914,13 @@ static char *Macro_Version (void)
q2 servers checking for cheats. */ q2 servers checking for cheats. */
return va("%.2f %s", 2.57, version_string()); return va("%.2f %s", 2.57, version_string());
} }
static char *Macro_Dedicated (void)
{
if (isDedicated)
return "1";
else
return "0";
}
static char *Macro_Quote (void) static char *Macro_Quote (void)
{ {
return "\""; return "\"";
@ -3973,6 +3996,7 @@ void Cmd_Init (void)
Cmd_AddMacro("properdate", Macro_ProperDate, false); Cmd_AddMacro("properdate", Macro_ProperDate, false);
Cmd_AddMacro("version", Macro_Version, false); Cmd_AddMacro("version", Macro_Version, false);
Cmd_AddMacro("qt", Macro_Quote, false); Cmd_AddMacro("qt", Macro_Quote, false);
Cmd_AddMacro("dedicated", Macro_Dedicated, false);
Cvar_Register(&tp_disputablemacros, "Teamplay"); Cvar_Register(&tp_disputablemacros, "Teamplay");

View file

@ -37,7 +37,7 @@ void Mod_UpdateCRC(void *ctx, void *data, size_t a, size_t b)
if (strcmp(st, Info_ValueForKey(cls.userinfo[0], ctx))) if (strcmp(st, Info_ValueForKey(cls.userinfo[0], ctx)))
{ {
Info_SetValueForKey (cls.userinfo[0], ctx, st, sizeof(cls.userinfo[0])); Info_SetValueForKey (cls.userinfo[0], ctx, st, sizeof(cls.userinfo[0]));
if (cls.state >= ca_connected) if (cls.state >= ca_connected && (cls.protocol == CP_QUAKEWORLD || (cls.fteprotocolextensions2 & PEXT2_PREDINFO)))
CL_SendClientCommand(true, "setinfo %s %s", (char*)ctx, st); CL_SendClientCommand(true, "setinfo %s %s", (char*)ctx, st);
} }
} }
@ -2519,6 +2519,7 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
char fname[MAX_QPATH]; char fname[MAX_QPATH];
char tok[64]; char tok[64];
size_t fsize; size_t fsize;
com_tokentype_t ttype;
Q_snprintfz(fname, sizeof(fname), "%s.framegroups", modelname); Q_snprintfz(fname, sizeof(fname), "%s.framegroups", modelname);
line = file = COM_LoadFile(fname, 5, &fsize); line = file = COM_LoadFile(fname, 5, &fsize);
if (!file) if (!file)
@ -2546,7 +2547,11 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
frames[count].loop = true; frames[count].loop = true;
else else
frames[count].loop = !!atoi(tok); frames[count].loop = !!atoi(tok);
line = COM_ParseOut(line, frames[count].name, sizeof(frames[count].name)); line = COM_ParseType(line, tok, sizeof(tok), &ttype);
if (ttype != TTP_EOF)
Q_strncpyz(frames[count].name, tok, sizeof(frames[count].name));
else
Q_snprintfz(frames[count].name, sizeof(frames[count].name), "groupified_%d_anim", count); //to match DP. frameforname cares.
if (frames[count].posecount>0 && frames[count].fps) if (frames[count].posecount>0 && frames[count].fps)
count++; count++;
@ -4519,7 +4524,15 @@ int Mod_TagNumForName(model_t *model, const char *name)
if (!model) if (!model)
return 0; return 0;
if (model->loadstate != MLS_LOADED) if (model->loadstate != MLS_LOADED)
return 0; {
if (model->loadstate == MLS_NOTLOADED)
Mod_LoadModel(model, MLV_SILENT);
if (model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
if (model->loadstate != MLS_LOADED)
return 0;
}
#ifdef HALFLIFEMODELS #ifdef HALFLIFEMODELS
if (model->type == mod_halflife) if (model->type == mod_halflife)
return HLMDL_BoneForName(model, name); return HLMDL_BoneForName(model, name);
@ -4624,8 +4637,15 @@ int Mod_SkinNumForName(model_t *model, int surfaceidx, const char *name)
galiasinfo_t *inf; galiasinfo_t *inf;
galiasskin_t *skin; galiasskin_t *skin;
if (!model || model->type != mod_alias) if (!model || model->loadstate != MLS_LOADED)
return -1; {
if (model && model->loadstate == MLS_NOTLOADED)
Mod_LoadModel(model, MLV_SILENT);
if (model && model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
if (!model || model->loadstate != MLS_LOADED || model->type != mod_alias)
return -1;
}
inf = Mod_Extradata(model); inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf) while(surfaceidx-->0 && inf)
@ -4648,8 +4668,15 @@ const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num)
galiasanimation_t *group; galiasanimation_t *group;
galiasinfo_t *inf; galiasinfo_t *inf;
if (!model) if (!model || model->loadstate != MLS_LOADED)
return NULL; {
if (model && model->loadstate == MLS_NOTLOADED)
Mod_LoadModel(model, MLV_SILENT);
if (model && model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
if (!model || model->loadstate != MLS_LOADED)
return NULL;
}
if (model->type == mod_alias) if (model->type == mod_alias)
{ {
inf = Mod_Extradata(model); inf = Mod_Extradata(model);
@ -4674,8 +4701,15 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam
galiasanimation_t *group; galiasanimation_t *group;
galiasinfo_t *inf; galiasinfo_t *inf;
if (!model) if (!model || model->loadstate != MLS_LOADED)
return false; {
if (model && model->loadstate == MLS_NOTLOADED)
Mod_LoadModel(model, MLV_SILENT);
if (model && model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
if (!model || model->loadstate != MLS_LOADED)
return false;
}
if (model->type == mod_alias) if (model->type == mod_alias)
{ {
inf = Mod_Extradata(model); inf = Mod_Extradata(model);
@ -4706,21 +4740,32 @@ shader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num)
galiasinfo_t *inf; galiasinfo_t *inf;
galiasskin_t *skin; galiasskin_t *skin;
if (!model || model->type != mod_alias) if (!model || model->loadstate != MLS_LOADED)
{ {
if (model->type == mod_brush && surfaceidx < model->numtextures && !num) if (model && model->loadstate == MLS_NOTLOADED)
return model->textures[surfaceidx]->shader; Mod_LoadModel(model, MLV_SILENT);
return NULL; if (model && model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
if (!model || model->loadstate != MLS_LOADED)
return NULL;
} }
inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf) if (model->type == mod_alias)
inf = inf->nextsurf; {
inf = Mod_Extradata(model);
if (!inf || num >= inf->numskins) while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (!inf || num >= inf->numskins)
return NULL;
skin = inf->ofsskins;
return skin[num].frame[0].shader;
}
else if (model->type == mod_brush && surfaceidx < model->numtextures && !num)
return model->textures[surfaceidx]->shader;
else
return NULL; return NULL;
skin = inf->ofsskins;
return skin[num].frame[0].shader;
} }
#endif #endif
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num) const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num)

View file

@ -179,6 +179,14 @@ typedef struct link_s
struct link_s *prev, *next; struct link_s *prev, *next;
} link_t; } link_t;
#ifdef USEAREAGRID
typedef struct
{
link_t l;
void *ed;
} areagridlink_t;
#endif
void ClearLink (link_t *l); void ClearLink (link_t *l);
void RemoveLink (link_t *l); void RemoveLink (link_t *l);

View file

@ -794,7 +794,7 @@ cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
Info_SetValueForKey (cls.userinfo[0], var->name, value, sizeof(cls.userinfo[0])); Info_SetValueForKey (cls.userinfo[0], var->name, value, sizeof(cls.userinfo[0]));
if (cls.state >= ca_connected) if (cls.state >= ca_connected)
{ {
#ifdef Q2CLIENT #if defined(Q2CLIENT) || defined(Q3CLIENT)
if (cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3) //q2 just resends the lot. Kinda bad... if (cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3) //q2 just resends the lot. Kinda bad...
{ {
cls.resendinfo = true; cls.resendinfo = true;

View file

@ -786,7 +786,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void
|| !Q_strcasecmp(link, "ent") || !Q_strcasecmp(link, "rtlights") || !Q_strcasecmp(link, "ent") || !Q_strcasecmp(link, "rtlights")
|| !Q_strcasecmp(link, "shader") || !Q_strcasecmp(link, "framegroups")) || !Q_strcasecmp(link, "shader") || !Q_strcasecmp(link, "framegroups"))
Q_snprintfz(link, sizeof(link), "\\edit\\%s", name); Q_snprintfz(link, sizeof(link), "\\edit\\%s", name);
else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "pcx")|| !Q_strcasecmp(link, "bmp")) else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "pcx") || !Q_strcasecmp(link, "bmp") || !Q_strcasecmp(link, "dds"))
{ {
//FIXME: image replacements are getting in the way here. //FIXME: image replacements are getting in the way here.
Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name); Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name);
@ -3026,7 +3026,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*stuff that makes dp-only mods work a bit better*/ /*stuff that makes dp-only mods work a bit better*/
#define DPCOMPAT QCFG "gl_specular 1\nset _cl_playermodel \"\"\n set dpcompat_set 1\ndpcompat_console 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n" #define DPCOMPAT QCFG "gl_specular 1\nset _cl_playermodel \"\"\n set dpcompat_set 1\ndpcompat_console 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n"
/*nexuiz/xonotic has a few quirks/annoyances...*/ /*nexuiz/xonotic has a few quirks/annoyances...*/
#define NEXCFG DPCOMPAT "cl_nopred 1\ncl_loopbackprotocol dpp7\nset sv_listen_dp 1\nset sv_listen_qw 0\nset sv_listen_nq 0\nset dpcompat_nopreparse 1\nset r_particlesdesc effectinfo\nset sv_bigcoords 1\nset sv_maxairspeed \"400\"\nset sv_jumpvelocity 270\nset sv_mintic \"0.01\"\ncl_nolerp 0\npr_enable_uriget 0\n" #define NEXCFG DPCOMPAT "cl_nopred 1\ncl_loopbackprotocol dpp7\nset sv_listen_dp 1\nset sv_listen_qw 0\nset sv_listen_nq 0\nset dpcompat_nopreparse 1\nset r_particlesdesc effectinfo\nset sv_bigcoords 1\nset sv_maxairspeed \"30\"\nset sv_jumpvelocity 270\nset sv_mintic \"0.01\"\ncl_nolerp 0\npr_enable_uriget 0\n"
#define XONCFG NEXCFG "set qport $qport_\ncom_parseutf8 1\npr_fixbrokenqccarrays 2\n" #define XONCFG NEXCFG "set qport $qport_\ncom_parseutf8 1\npr_fixbrokenqccarrays 2\n"
/*some modern non-compat settings*/ /*some modern non-compat settings*/
#define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n" #define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n"

View file

@ -4115,6 +4115,7 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole
i = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (mod->lightmaps.width*mod->lightmaps.height*3); i = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (mod->lightmaps.width*mod->lightmaps.height*3);
mod->lightmaps.deluxemapping = !(i&1); mod->lightmaps.deluxemapping = !(i&1);
mod->lightmaps.count = max(mod->lightmaps.count, i); mod->lightmaps.count = max(mod->lightmaps.count, i);
mod->lightmaps.deluxemapping_modelspace = true; //we assume true for q3bsp.
for (i = 0; i < mod->numsurfaces && mod->lightmaps.deluxemapping; i++) for (i = 0; i < mod->numsurfaces && mod->lightmaps.deluxemapping; i++)
{ {
@ -4159,6 +4160,28 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole
mod->funcs.NativeContents = CM_NativeContents; mod->funcs.NativeContents = CM_NativeContents;
#ifndef SERVERONLY #ifndef SERVERONLY
{
char deluxeMaps[64], *key;
key = (char*)Mod_ParseWorldspawnKey(mod, "deluxeMaps", deluxeMaps, sizeof(deluxeMaps));
if (*key)
{
switch(atoi(key))
{
case 0:
mod->lightmaps.deluxemapping = false;
break;
case 1:
// mod->lightmaps.deluxemapping = true;
mod->lightmaps.deluxemapping_modelspace = true;
break;
case 2:
// mod->lightmaps.deluxemapping = true;
mod->lightmaps.deluxemapping_modelspace = false;
break;
}
}
}
//light grid info //light grid info
if (mod->lightgrid) if (mod->lightgrid)
{ {
@ -6600,7 +6623,4 @@ void CM_Init(void) //register cvars.
CM_InitBoxHull (); CM_InitBoxHull ();
} }
void CM_Shutdown(void)
{
}
#endif #endif

View file

@ -16,6 +16,7 @@ static char *cvargroup_progs = "Progs variables";
cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods."); cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods.");
cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods."); cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods.");
cvar_t dpcompat_findradiusarealinks = CVARD("dpcompat_findradiusarealinks", "0", "Use the world collision info to accelerate findradius instead of looping through every single entity. May actually be slower for large radiuses, or fail to find entities which have not been linked properly with setorigin.");
cvar_t pr_droptofloorunits = CVARD("pr_droptofloorunits", "256", "Distance that droptofloor is allowed to drop to be considered successul."); cvar_t pr_droptofloorunits = CVARD("pr_droptofloorunits", "256", "Distance that droptofloor is allowed to drop to be considered successul.");
cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0"); cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0");
cvar_t pr_fixbrokenqccarrays = CVARFD("pr_fixbrokenqccarrays", "0", CVAR_LATCH, "As part of its nq/qw/h2/csqc support, FTE remaps QC fields to match an internal order. This is a faster way to handle extended fields. However, some QCCs are buggy and don't report all field defs.\n0: do nothing. QCC must be well behaved.\n1: Duplicate engine fields, remap the ones we can to known offsets. This is sufficient for QCCX/FrikQCC mods that use hardcoded or even occasional calculated offsets (fixes ktpro).\n2: Scan the mod for field accessing instructions, and assume those are the fields (and that they don't alias non-fields). This can be used to work around gmqcc's WTFs (fixes xonotic)."); cvar_t pr_fixbrokenqccarrays = CVARFD("pr_fixbrokenqccarrays", "0", CVAR_LATCH, "As part of its nq/qw/h2/csqc support, FTE remaps QC fields to match an internal order. This is a faster way to handle extended fields. However, some QCCs are buggy and don't report all field defs.\n0: do nothing. QCC must be well behaved.\n1: Duplicate engine fields, remap the ones we can to known offsets. This is sufficient for QCCX/FrikQCC mods that use hardcoded or even occasional calculated offsets (fixes ktpro).\n2: Scan the mod for field accessing instructions, and assume those are the fields (and that they don't alias non-fields). This can be used to work around gmqcc's WTFs (fixes xonotic).");
@ -45,6 +46,7 @@ void PF_Common_RegisterCvars(void)
Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs);
Cvar_Register (&sv_gameplayfix_nolinknonsolid, cvargroup_progs); Cvar_Register (&sv_gameplayfix_nolinknonsolid, cvargroup_progs);
Cvar_Register (&dpcompat_findradiusarealinks, cvargroup_progs);
Cvar_Register (&pr_droptofloorunits, cvargroup_progs); Cvar_Register (&pr_droptofloorunits, cvargroup_progs);
Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs); Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs);
Cvar_Register (&pr_tempstringcount, cvargroup_progs); Cvar_Register (&pr_tempstringcount, cvargroup_progs);
@ -64,6 +66,18 @@ void PF_Common_RegisterCvars(void)
WPhys_Init(); WPhys_Init();
} }
qofs_t PR_ReadBytesString(char *str)
{
size_t u = strtoul(str, &str, 0);
if (*str == 'g')
u *= 1024*1024*1024;
if (*str == 'm')
u *= 1024*1024;
if (*str == 'k')
u *= 1024;
return u;
}
//just prints out a warning with stack trace. so I can throttle spammy stack traces. //just prints out a warning with stack trace. so I can throttle spammy stack traces.
static void PF_Warningf(pubprogfuncs_t *prinst, const char *fmt, ...) static void PF_Warningf(pubprogfuncs_t *prinst, const char *fmt, ...)
{ {
@ -1895,6 +1909,8 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
int i; int i;
size_t insize; size_t insize;
Con_DPrintf("qcfopen(\"%s\", %i) called\n", name, fmode);
for (i = 0; i < MAX_QC_FILES; i++) for (i = 0; i < MAX_QC_FILES; i++)
if (!pf_fopen_files[i].data) if (!pf_fopen_files[i].data)
break; break;
@ -2849,10 +2865,14 @@ Returns a chain of entities that have origins within a spherical area
findradius (origin, radius) findradius (origin, radius)
================= =================
*/ */
#define AREA_ALL 0
#define AREA_SOLID 1
#define AREA_TRIGGER 2
void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
world_t *w = prinst->parms->user; world_t *w = prinst->parms->user;
extern cvar_t sv_gameplayfix_blowupfallenzombies; extern cvar_t sv_gameplayfix_blowupfallenzombies;
extern cvar_t dpcompat_findradiusarealinks;
wedict_t *ent, *chain; wedict_t *ent, *chain;
float rad; float rad;
float *org; float *org;
@ -2864,27 +2884,60 @@ void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
org = G_VECTOR(OFS_PARM0); org = G_VECTOR(OFS_PARM0);
rad = G_FLOAT(OFS_PARM1); rad = G_FLOAT(OFS_PARM1);
rad = rad*rad;
if (prinst->callargc > 2) if (prinst->callargc > 2)
f = G_INT(OFS_PARM2)+prinst->fieldadjust; f = G_INT(OFS_PARM2)+prinst->fieldadjust;
else else
f = &((comentvars_t*)NULL)->chain - (int*)NULL; f = &((comentvars_t*)NULL)->chain - (int*)NULL;
for (i=1 ; i<w->num_edicts ; i++) if (dpcompat_findradiusarealinks.ival)
{ {
ent = WEDICT_NUM(prinst, i); static wedict_t *nearent[32768];
if (ED_ISFREE(ent)) vec3_t mins, maxs;
continue; int numents;
if (ent->v->solid == SOLID_NOT && (!((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) extern int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype);
continue;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
if (DotProduct(eorg,eorg) > rad)
continue;
((int*)ent->v)[f] = EDICT_TO_PROG(prinst, chain); mins[0] = org[0] - rad;
chain = ent; mins[1] = org[1] - rad;
mins[2] = org[2] - rad;
maxs[0] = org[0] + rad;
maxs[1] = org[1] + rad;
maxs[2] = org[2] + rad;
numents = World_AreaEdicts(w, mins, maxs, nearent, countof(nearent), AREA_ALL);
rad = rad*rad;
for (i=0 ; i<numents ; i++)
{
ent = nearent[i];
if (ent->v->solid == SOLID_NOT && (!((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)
continue;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
if (DotProduct(eorg,eorg) > rad)
continue;
((int*)ent->v)[f] = EDICT_TO_PROG(prinst, chain);
chain = ent;
}
}
else
{
rad = rad*rad;
for (i=1 ; i<w->num_edicts ; i++)
{
ent = WEDICT_NUM(prinst, i);
if (ED_ISFREE(ent))
continue;
if (ent->v->solid == SOLID_NOT && (!((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)
continue;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
if (DotProduct(eorg,eorg) > rad)
continue;
((int*)ent->v)[f] = EDICT_TO_PROG(prinst, chain);
chain = ent;
}
} }
RETURN_EDICT(prinst, chain); RETURN_EDICT(prinst, chain);
@ -4903,6 +4956,41 @@ void QCBUILTIN PF_crossproduct (pubprogfuncs_t *prinst, struct globalvars_s *pr_
//Maths functions //Maths functions
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
#ifndef NOLEGACY
unsigned int FTEToDPContents(unsigned int contents)
{
unsigned int r = 0;
if (contents & FTECONTENTS_SOLID)
r |= DPCONTENTS_SOLID;
if (contents & FTECONTENTS_WATER)
r |= DPCONTENTS_WATER;
if (contents & FTECONTENTS_SLIME)
r |= DPCONTENTS_SLIME;
if (contents & FTECONTENTS_LAVA)
r |= DPCONTENTS_LAVA;
if (contents & FTECONTENTS_SKY)
r |= DPCONTENTS_SKY;
if (contents & FTECONTENTS_BODY)
r |= DPCONTENTS_BODY;
if (contents & FTECONTENTS_CORPSE)
r |= DPCONTENTS_CORPSE;
if (contents & Q3CONTENTS_NODROP)
r |= DPCONTENTS_NODROP;
if (contents & FTECONTENTS_PLAYERCLIP)
r |= DPCONTENTS_PLAYERCLIP;
if (contents & FTECONTENTS_MONSTERCLIP)
r |= DPCONTENTS_MONSTERCLIP;
if (contents & Q3CONTENTS_DONOTENTER)
r |= DPCONTENTS_DONOTENTER;
if (contents & Q3CONTENTS_BOTCLIP)
r |= DPCONTENTS_BOTCLIP;
// if (contents & FTECONTENTS_OPAQUE)
// r |= DPCONTENTS_OPAQUE;
return r;
}
#endif
/* /*
=============== ===============
PF_droptofloor PF_droptofloor
@ -5742,7 +5830,7 @@ finished:
void QCBUILTIN PF_sprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_sprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
char outbuf[4096]; char outbuf[65536]; //FIXME: no idea how big this actually needs to be.
PF_sprintf_internal(prinst, pr_globals, PR_GetStringOfs(prinst, OFS_PARM0), 1, outbuf, sizeof(outbuf)); PF_sprintf_internal(prinst, pr_globals, PR_GetStringOfs(prinst, OFS_PARM0), 1, outbuf, sizeof(outbuf));
RETURN_TSTRING(outbuf); RETURN_TSTRING(outbuf);
} }

View file

@ -4,6 +4,8 @@ extern "C" {
#include "progtype.h" #include "progtype.h"
#include "progslib.h" #include "progslib.h"
#define AREAGRIDPERENT 16
struct wedict_s struct wedict_s
{ {
enum ereftype_e ereftype; enum ereftype_e ereftype;
@ -21,7 +23,12 @@ struct wedict_s
}; };
#endif #endif
/*the above is shared with qclib*/ /*the above is shared with qclib*/
#ifdef USEAREAGRID
areagridlink_t gridareas[AREAGRIDPERENT]; //on overflow, use the inefficient overflow list.
size_t gridareasequence; //used to avoid iterrating the same ent twice.
#else
link_t area; link_t area;
#endif
pvscache_t pvsinfo; pvscache_t pvsinfo;
int lastruntime; int lastruntime;
int solidsize; int solidsize;
@ -516,6 +523,10 @@ void PF_WriteString_Internal (int target, const char *str);
pbool QDECL ED_CanFree (edict_t *ed); pbool QDECL ED_CanFree (edict_t *ed);
#endif #endif
#ifndef NOLEGACY
unsigned int FTEToDPContents(unsigned int contents);
#endif
#define MOVETYPE_NONE 0 // never moves #define MOVETYPE_NONE 0 // never moves
#define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLENOCLIP 1
#define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_ANGLECLIP 2
@ -589,6 +600,8 @@ typedef struct
} rbeplugfuncs_t; } rbeplugfuncs_t;
#define RBEPLUGFUNCS_VERSION 1 #define RBEPLUGFUNCS_VERSION 1
qofs_t PR_ReadBytesString(char *str);
#define DAMAGE_NO 0 #define DAMAGE_NO 0
#define DAMAGE_YES 1 #define DAMAGE_YES 1

View file

@ -1082,22 +1082,22 @@ typedef struct entity_state_s
qbyte basebone; qbyte basebone;
qbyte pad; qbyte pad;
unsigned int skinnum; /*q2 needs 32 bits, which is quite impressive*/ unsigned int skinnum; /*for q2 this often contains rgba*/
unsigned short colormap; unsigned short colormap;
qbyte glowsize; qbyte glowsize;
qbyte glowcolour; qbyte glowcolour;
qbyte scale; qbyte scale; //4.4 precision
char fatness; char fatness;
qbyte hexen2flags; qbyte hexen2flags;
qbyte abslight; qbyte abslight;
qbyte dpflags; qbyte dpflags;
qbyte colormod[3];//multiply this by 8 to read as 0 to 1... qbyte colormod[3];//3.5 precision
qbyte glowmod[3]; qbyte glowmod[3]; //3.5 precision
qbyte trans; qbyte trans; //254==1, 255==1-or-wateralpha
unsigned short light[4]; unsigned short light[4];

View file

@ -132,6 +132,28 @@ typedef struct q2trace_s
#define MOVE_OTHERONLY (1<<8) //test the trace against a single entity, ignoring non-solid/owner/etc flags (but respecting contents). #define MOVE_OTHERONLY (1<<8) //test the trace against a single entity, ignoring non-solid/owner/etc flags (but respecting contents).
#define MOVE_IGNOREHULL (1u<<31) //used on tracelines etc to simplify the code a little #define MOVE_IGNOREHULL (1u<<31) //used on tracelines etc to simplify the code a little
#ifdef USEAREAGRID
//this macro does it in two steps to avoid float precision issues.
//it also ensures that it will return at least one grid section.
#define CALCAREAGRIDBOUNDS(w,min,max) \
ming[0] = floor(((min)[0]+(w)->gridbias[0]) / (w)->gridscale[0]); \
ming[1] = floor(((min)[1]+(w)->gridbias[1]) / (w)->gridscale[1]); \
maxg[0] = floor(((max)[0]+(w)->gridbias[0]) / (w)->gridscale[0]); \
maxg[1] = floor(((max)[1]+(w)->gridbias[1]) / (w)->gridscale[1]); \
ming[0] = bound(0, ming[0], (w)->gridsize[0]-1); \
ming[1] = bound(0, ming[1], (w)->gridsize[1]-1); \
maxg[0] = bound(ming[0], maxg[0], (w)->gridsize[0]-1)+1; \
maxg[1] = bound(ming[1], maxg[1], (w)->gridsize[1]-1)+1;
#else
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,wedict_t,area)
#endif
#if defined(Q2SERVER) || !defined(USEAREAGRID)
//q2 game code embeds a link_t struct inside the public edicts.
//this is why we can't have nice things.
//a binary tree. ents straddling a node are inserted into the parent.
//this is a problem when your root note passes through '0 0 0' and you have shitty mods with 2000 such ents.
typedef struct areanode_s typedef struct areanode_s
{ {
int axis; // -1 = leaf node int axis; // -1 = leaf node
@ -139,8 +161,7 @@ typedef struct areanode_s
struct areanode_s *children[2]; struct areanode_s *children[2];
link_t edicts; link_t edicts;
} areanode_t; } areanode_t;
#endif
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,wedict_t,area)
typedef struct wedict_s wedict_t; typedef struct wedict_s wedict_t;
#define PROG_TO_WEDICT (wedict_t*)PROG_TO_EDICT #define PROG_TO_WEDICT (wedict_t*)PROG_TO_EDICT
@ -189,10 +210,23 @@ struct world_s
struct pubprogfuncs_s *progs; struct pubprogfuncs_s *progs;
qboolean usesolidcorpse; //to disable SOLID_CORPSE when running hexen2 due to conflict. qboolean usesolidcorpse; //to disable SOLID_CORPSE when running hexen2 due to conflict.
model_t *worldmodel; model_t *worldmodel;
#ifdef USEAREAGRID
vec2_t gridbias;
vec2_t gridscale;
size_t gridsize[2];
areagridlink_t *gridareas; //[gridsize[0]*gridsize[1]]
areagridlink_t jumboarea; //node containing ents too large to fit.
areagridlink_t portallist;
#else
areanode_t portallist;
#endif
#if defined(Q2SERVER) || !defined(USEAREAGRID)
areanode_t *areanodes; areanode_t *areanodes;
int areanodedepth; int areanodedepth;
int numareanodes; int numareanodes;
areanode_t portallist; #endif
double physicstime; // the last time global physics were run double physicstime; // the last time global physics were run
unsigned int framenum; unsigned int framenum;
@ -263,6 +297,7 @@ void World_RBE_Shutdown(world_t *world);
void World_ClearWorld (world_t *w, qboolean relink); void World_ClearWorld (world_t *w, qboolean relink);
// called after the world model has been loaded, before linking any entities // called after the world model has been loaded, before linking any entities
void World_ClearWorld_Nodes (world_t *w, qboolean relink); //legacy code for q2 compat.
void World_UnlinkEdict (wedict_t *ent); void World_UnlinkEdict (wedict_t *ent);
// call before removing an entity, and before trying to move one, // call before removing an entity, and before trying to move one,
@ -275,7 +310,13 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers);
// sets ent->v.absmin and ent->v.absmax // sets ent->v.absmin and ent->v.absmax
// if touchtriggers, calls prog functions for the intersected triggers // if touchtriggers, calls prog functions for the intersected triggers
#ifdef USEAREAGRID
void World_TouchAllLinks (world_t *w, wedict_t *ent);
extern size_t areagridsequence;
#else
void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node); void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node);
#define World_TouchAllLinks(w,e) World_TouchLinks(w,e,(w)->areanodes)
#endif
int World_PointContents (world_t *w, vec3_t p); int World_PointContents (world_t *w, vec3_t p);
// returns the CONTENTS_* value from the world at the given point. // returns the CONTENTS_* value from the world at the given point.

View file

@ -161,6 +161,8 @@ typedef struct
vec4_t e_lmscale[4]; vec4_t e_lmscale[4];
vec4_t e_uppercolour; vec4_t e_uppercolour;
vec4_t e_lowercolour; vec4_t e_lowercolour;
vec4_t e_colourmod;
vec4_t e_glowmod;
} cbuf_entity_t; } cbuf_entity_t;
//vertex attributes //vertex attributes
@ -250,6 +252,7 @@ typedef struct
ID3D11Buffer *stream_buffer[D3D11_BUFF_MAX]; ID3D11Buffer *stream_buffer[D3D11_BUFF_MAX];
unsigned int stream_stride[D3D11_BUFF_MAX]; unsigned int stream_stride[D3D11_BUFF_MAX];
unsigned int stream_offset[D3D11_BUFF_MAX]; unsigned int stream_offset[D3D11_BUFF_MAX];
qboolean stream_rgbaf;
program_t *programfixedemu[4]; program_t *programfixedemu[4];
@ -1056,6 +1059,12 @@ static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass)
case T_GEN_FULLBRIGHT: case T_GEN_FULLBRIGHT:
BindTexture(tu, shaderstate.curtexnums->fullbright); BindTexture(tu, shaderstate.curtexnums->fullbright);
break; break;
case T_GEN_REFLECTCUBE:
BindTexture(tu, shaderstate.curtexnums->reflectcube);
break;
case T_GEN_REFLECTMASK:
BindTexture(tu, shaderstate.curtexnums->reflectmask);
break;
case T_GEN_ANIMMAP: case T_GEN_ANIMMAP:
BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]); BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]);
break; break;
@ -1414,6 +1423,7 @@ static void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pa
const mesh_t *m = shaderstate.meshlist[0]; const mesh_t *m = shaderstate.meshlist[0];
if (pass->flags & SHADER_PASS_NOCOLORARRAY) if (pass->flags & SHADER_PASS_NOCOLORARRAY)
{ {
#if 1
ID3D11Buffer *buf; ID3D11Buffer *buf;
unsigned char *map; unsigned char *map;
unsigned int offset; unsigned int offset;
@ -1429,6 +1439,25 @@ static void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pa
shaderstate.stream_buffer[D3D11_BUFF_COL] = buf; shaderstate.stream_buffer[D3D11_BUFF_COL] = buf;
shaderstate.stream_offset[D3D11_BUFF_COL] = offset; shaderstate.stream_offset[D3D11_BUFF_COL] = offset;
shaderstate.stream_stride[D3D11_BUFF_COL] = 0; //omg that's so lame! shaderstate.stream_stride[D3D11_BUFF_COL] = 0; //omg that's so lame!
shaderstate.stream_rgbaf = false;
#else
ID3D11Buffer *buf;
unsigned char *map;
unsigned int offset;
vec4_t passcolour;
static vec4_t fakesource = {1,1,1,1};
allocvertexbuffer(&buf, &offset, (void**)&map, sizeof(fakesource));
colourgen(pass, 1, (vec4_t*)&fakesource, NULL, (vec4_t*)&passcolour, m);
alphagen(pass, 1, (vec4_t*)&fakesource, NULL, (vec4_t*)&passcolour, m);
Vector4Copy(passcolour, map);
ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);
shaderstate.stream_buffer[D3D11_BUFF_COL] = buf;
shaderstate.stream_offset[D3D11_BUFF_COL] = offset;
shaderstate.stream_stride[D3D11_BUFF_COL] = 0; //omg that's so lame!
shaderstate.stream_rgbaf = true;
#endif
} }
else else
{ {
@ -1441,6 +1470,7 @@ static void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pa
shaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff; shaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff;
shaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs; shaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs;
shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(vbovdata_t); shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(vbovdata_t);
shaderstate.stream_rgbaf = false;
} }
else else
{ {
@ -1460,6 +1490,7 @@ static void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pa
shaderstate.stream_buffer[D3D11_BUFF_COL] = buf; shaderstate.stream_buffer[D3D11_BUFF_COL] = buf;
shaderstate.stream_offset[D3D11_BUFF_COL] = offset; shaderstate.stream_offset[D3D11_BUFF_COL] = offset;
shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t); shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t);
shaderstate.stream_rgbaf = false;
} }
} }
} }
@ -1888,7 +1919,7 @@ static void BE_ApplyUniforms(program_t *prog, int permu)
shaderstate.lcbuffer //light buffer that changes rarelyish shaderstate.lcbuffer //light buffer that changes rarelyish
}; };
//FIXME: how many of these calls can we avoid? //FIXME: how many of these calls can we avoid?
ID3D11DeviceContext_IASetInputLayout(d3ddevctx, prog->permu[permu].h.hlsl.layout); ID3D11DeviceContext_IASetInputLayout(d3ddevctx, prog->permu[permu].h.hlsl.layouts[shaderstate.stream_rgbaf]);
ID3D11DeviceContext_VSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.vert, NULL, 0); ID3D11DeviceContext_VSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.vert, NULL, 0);
ID3D11DeviceContext_HSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.hull, NULL, 0); ID3D11DeviceContext_HSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.hull, NULL, 0);
ID3D11DeviceContext_DSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.domain, NULL, 0); ID3D11DeviceContext_DSSetShader(d3ddevctx, prog->permu[permu].h.hlsl.domain, NULL, 0);
@ -2230,6 +2261,7 @@ static void BE_DrawMeshChain_Internal(void)
shaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff; shaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff;
shaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs; shaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs;
shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(vbovdata_t); shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(vbovdata_t);
shaderstate.stream_rgbaf = false;
shaderstate.stream_buffer[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.buff; shaderstate.stream_buffer[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.buff;
shaderstate.stream_offset[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.offs; shaderstate.stream_offset[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.offs;
shaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(vbovdata_t); shaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(vbovdata_t);
@ -2251,19 +2283,19 @@ static void BE_DrawMeshChain_Internal(void)
if (shaderstate.meshlist[0]->colors4f_array[0]) if (shaderstate.meshlist[0]->colors4f_array[0])
{ {
byte_vec4_t *map; vec4_t *map;
allocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(byte_vec4_t)); allocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(*map));
for (mno = 0; mno < shaderstate.nummeshes; mno++) for (mno = 0; mno < shaderstate.nummeshes; mno++)
{ {
m = shaderstate.meshlist[mno]; m = shaderstate.meshlist[mno];
for (i = 0; i < m->numvertexes; i++) memcpy(map, m->colors4f_array[0], sizeof(*map)*m->numvertexes);
((int*)map)[i] = D3DCOLOR_COLORVALUE(m->colors4f_array[0][i][2], m->colors4f_array[0][i][1], m->colors4f_array[0][i][0], m->colors4f_array[0][i][3]);
map += m->numvertexes; map += m->numvertexes;
} }
ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0); ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);
shaderstate.stream_buffer[D3D11_BUFF_COL] = buf; shaderstate.stream_buffer[D3D11_BUFF_COL] = buf;
shaderstate.stream_offset[D3D11_BUFF_COL] = offset; shaderstate.stream_offset[D3D11_BUFF_COL] = offset;
shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t); shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(*map);
shaderstate.stream_rgbaf = true;
} }
else if (shaderstate.meshlist[0]->colors4b_array) else if (shaderstate.meshlist[0]->colors4b_array)
{ {
@ -2280,12 +2312,14 @@ static void BE_DrawMeshChain_Internal(void)
shaderstate.stream_buffer[D3D11_BUFF_COL] = buf; shaderstate.stream_buffer[D3D11_BUFF_COL] = buf;
shaderstate.stream_offset[D3D11_BUFF_COL] = offset; shaderstate.stream_offset[D3D11_BUFF_COL] = offset;
shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t); shaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t);
shaderstate.stream_rgbaf = false;
} }
else else
{ {
shaderstate.stream_buffer[D3D11_BUFF_COL] = 0; shaderstate.stream_buffer[D3D11_BUFF_COL] = 0;
shaderstate.stream_offset[D3D11_BUFF_COL] = 0; shaderstate.stream_offset[D3D11_BUFF_COL] = 0;
shaderstate.stream_stride[D3D11_BUFF_COL] = 0; shaderstate.stream_stride[D3D11_BUFF_COL] = 0;
shaderstate.stream_rgbaf = false;
} }
if (shaderstate.meshlist[0]->lmst_array[0]) if (shaderstate.meshlist[0]->lmst_array[0])
@ -2669,7 +2703,7 @@ static void BE_UploadLightmaps(qboolean force)
if (!lightmap[i]) if (!lightmap[i])
continue; continue;
if (force) if (force && !lightmap[i]->external)
{ {
lightmap[i]->rectchange.l = 0; lightmap[i]->rectchange.l = 0;
lightmap[i]->rectchange.t = 0; lightmap[i]->rectchange.t = 0;
@ -3001,6 +3035,9 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod)
R_FetchPlayerColour(e->topcolour, cbe->e_uppercolour); R_FetchPlayerColour(e->topcolour, cbe->e_uppercolour);
R_FetchPlayerColour(e->bottomcolour, cbe->e_lowercolour); R_FetchPlayerColour(e->bottomcolour, cbe->e_lowercolour);
R_FetchPlayerColour(e->bottomcolour, cbe->e_colourmod);
VectorCopy(e->shaderRGBAf, cbe->e_colourmod);
VectorCopy(e->glowmod, cbe->e_glowmod);cbe->e_glowmod[3] = 1;
//various stuff in modelspace //various stuff in modelspace
Matrix4x4_CM_Transform3(modelinv, r_origin, cbe->e_eyepos); Matrix4x4_CM_Transform3(modelinv, r_origin, cbe->e_eyepos);
@ -3138,12 +3175,13 @@ static void BE_SubmitMeshesSortList(batch_t *sortlist)
if (batch->shader->flags & SHADER_SKY) if (batch->shader->flags & SHADER_SKY)
{ {
if (!batch->shader->prog) if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)
{ {
if (shaderstate.mode == BEM_STANDARD) if (R_DrawSkyChain (batch))
R_DrawSkyChain (batch); continue;
continue;
} }
else if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)
continue;
} }
BE_SubmitBatch(batch); BE_SubmitBatch(batch);
@ -3611,7 +3649,7 @@ void D3D11BE_DrawWorld (batch_t **worldbatches)
shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
shaderstate.identitylighting = 1; shaderstate.identitylighting = r_lightmap_scale.value;
shaderstate.identitylighting *= r_refdef.hdr_value; shaderstate.identitylighting *= r_refdef.hdr_value;
// shaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival); // shaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);
@ -3619,23 +3657,25 @@ void D3D11BE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_OPAQUE);
#ifdef RTLIGHTS #ifdef RTLIGHTS
RSpeedRemark(); RSpeedRemark();
D3D11BE_SelectEntity(&r_worldentity); D3D11BE_SelectEntity(&r_worldentity);
Sh_DrawLights(r_refdef.scenevis); Sh_DrawLights(r_refdef.scenevis);
RSpeedEnd(RSPEED_STENCILSHADOWS); RSpeedEnd(RSPEED_RTLIGHTS);
#endif #endif
RSpeedRemark();
D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT); D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_TRANSPARENTS);
} }
else else
{ {
RSpeedRemark(); RSpeedRemark();
shaderstate.identitylighting = 1; shaderstate.identitylighting = 1;
D3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT); D3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES); RSpeedEnd(RSPEED_OPAQUE);
} }
R_RenderDlights (); R_RenderDlights ();

View file

@ -155,6 +155,8 @@ HRESULT STDMETHODCALLTYPE d3dinclude_Open(ID3DInclude *this, D3D_INCLUDE_TYPE In
"float4 e_lmscale[4];\n" "float4 e_lmscale[4];\n"
"float4 e_uppercolour;\n" "float4 e_uppercolour;\n"
"float4 e_lowercolour;\n" "float4 e_lowercolour;\n"
"float4 e_colourmod;\n"
"float4 e_glowmod;\n"
"};\n" "};\n"
"cbuffer fteviewdefs : register(b1)\n" "cbuffer fteviewdefs : register(b1)\n"
"{\n" "{\n"
@ -208,18 +210,21 @@ void D3D11Shader_DeleteProg(program_t *prog)
ID3D11InputLayout *layout; ID3D11InputLayout *layout;
ID3D11PixelShader *frag; ID3D11PixelShader *frag;
ID3D11VertexShader *vert; ID3D11VertexShader *vert;
unsigned int permu; unsigned int permu, l;
for (permu = 0; permu < countof(prog->permu); permu++) for (permu = 0; permu < countof(prog->permu); permu++)
{ {
vert = prog->permu[permu].h.hlsl.vert; vert = prog->permu[permu].h.hlsl.vert;
frag = prog->permu[permu].h.hlsl.frag; frag = prog->permu[permu].h.hlsl.frag;
layout = prog->permu[permu].h.hlsl.layout;
if (vert) if (vert)
ID3D11VertexShader_Release(vert); ID3D11VertexShader_Release(vert);
if (frag) if (frag)
ID3D11PixelShader_Release(frag); ID3D11PixelShader_Release(frag);
if (layout) for (l = 0; l < countof(prog->permu[permu].h.hlsl.layouts); l++)
ID3D11InputLayout_Release(layout); {
layout = prog->permu[permu].h.hlsl.layouts[l];
if (layout)
ID3D11InputLayout_Release(layout);
}
} }
} }
@ -231,6 +236,7 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int
void *gblob, size_t gsize, void *gblob, size_t gsize,
void *fblob, size_t fsize) void *fblob, size_t fsize)
{ {
int l;
qboolean success = true; qboolean success = true;
if (FAILED(ID3D11Device_CreateVertexShader(pD3DDev11, vblob, vsize, NULL, (ID3D11VertexShader**)&prog->permu[permu].h.hlsl.vert))) if (FAILED(ID3D11Device_CreateVertexShader(pD3DDev11, vblob, vsize, NULL, (ID3D11VertexShader**)&prog->permu[permu].h.hlsl.vert)))
@ -254,7 +260,7 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int
if (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, fblob, fsize, NULL, (ID3D11PixelShader**)&prog->permu[permu].h.hlsl.frag))) if (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, fblob, fsize, NULL, (ID3D11PixelShader**)&prog->permu[permu].h.hlsl.frag)))
success = false; success = false;
if (success) for (l = 0; l < 2 && success; l++)
{ {
D3D11_INPUT_ELEMENT_DESC decl[13]; D3D11_INPUT_ELEMENT_DESC decl[13];
int elements = 0; int elements = 0;
@ -288,7 +294,7 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int
decl[elements].SemanticName = "COLOR"; decl[elements].SemanticName = "COLOR";
decl[elements].SemanticIndex = 0; decl[elements].SemanticIndex = 0;
decl[elements].Format = DXGI_FORMAT_R8G8B8A8_UNORM; decl[elements].Format = l?DXGI_FORMAT_R32G32B32A32_FLOAT:DXGI_FORMAT_R8G8B8A8_UNORM;
decl[elements].InputSlot = D3D11_BUFF_COL; decl[elements].InputSlot = D3D11_BUFF_COL;
decl[elements].AlignedByteOffset = 0; decl[elements].AlignedByteOffset = 0;
decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
@ -341,7 +347,7 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int
decl[elements].InstanceDataStepRate = 0; decl[elements].InstanceDataStepRate = 0;
elements++; elements++;
*/ */
if (FAILED(ID3D11Device_CreateInputLayout(pD3DDev11, decl, elements, vblob, vsize, (ID3D11InputLayout**)&prog->permu[permu].h.hlsl.layout))) if (FAILED(ID3D11Device_CreateInputLayout(pD3DDev11, decl, elements, vblob, vsize, (ID3D11InputLayout**)&prog->permu[permu].h.hlsl.layouts[l])))
{ {
Con_Printf("HLSL Shader %s requires unsupported inputs\n", name); Con_Printf("HLSL Shader %s requires unsupported inputs\n", name);
success = false; success = false;
@ -450,7 +456,7 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned
prog->permu[permu].h.hlsl.hull = NULL; prog->permu[permu].h.hlsl.hull = NULL;
prog->permu[permu].h.hlsl.domain = NULL; prog->permu[permu].h.hlsl.domain = NULL;
prog->permu[permu].h.hlsl.geom = NULL; prog->permu[permu].h.hlsl.geom = NULL;
prog->permu[permu].h.hlsl.layout = NULL; memset(prog->permu[permu].h.hlsl.layouts, 0, sizeof(prog->permu[permu].h.hlsl.layouts));
if (pD3DCompile) if (pD3DCompile)
{ {
@ -732,6 +738,7 @@ qboolean D3D11Shader_Init(unsigned int flevel)
sh_config.pProgAutoFields = NULL; sh_config.pProgAutoFields = NULL;
sh_config.can_mipcap = true; //at creation time sh_config.can_mipcap = true; //at creation time
sh_config.havecubemaps = true;
// sh_config.tex_env_combine = 1; // sh_config.tex_env_combine = 1;
// sh_config.nv_tex_env_combine4 = 1; // sh_config.nv_tex_env_combine4 = 1;

View file

@ -3371,7 +3371,7 @@ void D3D8BE_DrawWorld (batch_t **worldbatches)
shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
shaderstate_identitylighting = 1; shaderstate_identitylighting = r_lightmap_scale.value;
shaderstate_identitylighting *= r_refdef.hdr_value; shaderstate_identitylighting *= r_refdef.hdr_value;
// shaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival); // shaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);
@ -3382,7 +3382,7 @@ void D3D8BE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
D3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); D3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_OPAQUE);
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (r_refdef.scenevis) if (r_refdef.scenevis)
@ -3390,19 +3390,21 @@ void D3D8BE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
D3D8BE_SelectEntity(&r_worldentity); D3D8BE_SelectEntity(&r_worldentity);
Sh_DrawLights(r_refdef.scenevis); Sh_DrawLights(r_refdef.scenevis);
RSpeedEnd(RSPEED_STENCILSHADOWS); RSpeedEnd(RSPEED_RTLIGHTS);
} }
#endif #endif
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
RSpeedRemark();
D3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT); D3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_TRANSPARENTS);
} }
else else
{ {
RSpeedRemark(); RSpeedRemark();
D3D8BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT); D3D8BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES); RSpeedEnd(RSPEED_OPAQUE);
} }
R_RenderDlights (); R_RenderDlights ();

View file

@ -765,6 +765,12 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass)
case T_GEN_FULLBRIGHT: case T_GEN_FULLBRIGHT:
BindTexture(tu, shaderstate.curtexnums->fullbright); BindTexture(tu, shaderstate.curtexnums->fullbright);
break; break;
case T_GEN_REFLECTCUBE:
BindTexture(tu, shaderstate.curtexnums->reflectcube);
break;
case T_GEN_REFLECTMASK:
BindTexture(tu, shaderstate.curtexnums->reflectmask);
break;
case T_GEN_ANIMMAP: case T_GEN_ANIMMAP:
BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]); BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]);
break; break;
@ -3146,12 +3152,13 @@ static void BE_SubmitMeshesSortList(batch_t *sortlist)
if (batch->shader->flags & SHADER_SKY) if (batch->shader->flags & SHADER_SKY)
{ {
if (!batch->shader->prog) if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)
{ {
if (shaderstate.mode == BEM_STANDARD) if (R_DrawSkyChain (batch))
R_DrawSkyChain (batch); continue;
continue;
} }
else if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)
continue;
} }
BE_SubmitBatch(batch); BE_SubmitBatch(batch);
@ -3546,7 +3553,7 @@ void D3D9BE_DrawWorld (batch_t **worldbatches)
shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
shaderstate_identitylighting = 1; shaderstate_identitylighting = r_lightmap_scale.value;
shaderstate_identitylighting *= r_refdef.hdr_value; shaderstate_identitylighting *= r_refdef.hdr_value;
// shaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival); // shaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);
@ -3557,7 +3564,7 @@ void D3D9BE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_OPAQUE);
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (r_refdef.scenevis) if (r_refdef.scenevis)
@ -3565,19 +3572,21 @@ void D3D9BE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
D3D9BE_SelectEntity(&r_worldentity); D3D9BE_SelectEntity(&r_worldentity);
Sh_DrawLights(r_refdef.scenevis); Sh_DrawLights(r_refdef.scenevis);
RSpeedEnd(RSPEED_STENCILSHADOWS); RSpeedEnd(RSPEED_RTLIGHTS);
} }
#endif #endif
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
RSpeedRemark();
D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT); D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_TRANSPARENTS);
} }
else else
{ {
RSpeedRemark(); RSpeedRemark();
D3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT); D3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES); RSpeedEnd(RSPEED_OPAQUE);
} }
R_RenderDlights (); R_RenderDlights ();

View file

@ -14,7 +14,7 @@ void D3D9_DestroyTexture (texid_t tex)
return; return;
if (tex->ptr) if (tex->ptr)
IDirect3DTexture9_Release((IDirect3DTexture9*)tex->ptr); IDirect3DBaseTexture9_Release((IDirect3DBaseTexture9*)tex->ptr);
tex->ptr = NULL; tex->ptr = NULL;
} }
@ -25,7 +25,7 @@ qboolean D3D9_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mip
D3DLOCKED_RECT lock; D3DLOCKED_RECT lock;
D3DFORMAT fmt; D3DFORMAT fmt;
D3DSURFACE_DESC desc; D3DSURFACE_DESC desc;
IDirect3DTexture9 *dt; IDirect3DBaseTexture9 *dbt;
qboolean swap = false; qboolean swap = false;
unsigned int pixelsize = 4; unsigned int pixelsize = 4;
unsigned int blocksize = 0; unsigned int blocksize = 0;
@ -90,52 +90,104 @@ qboolean D3D9_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mip
if (!pD3DDev9) if (!pD3DDev9)
return false; //can happen on errors return false; //can happen on errors
if (FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, mips->mip[0].width, mips->mip[0].height, mips->mipcount, 0, fmt, D3DPOOL_MANAGED, &dt, NULL))) if (mips->type == PTI_CUBEMAP)
return false;
for (i = 0; i < mips->mipcount; i++)
{ {
IDirect3DTexture9_GetLevelDesc(dt, i, &desc); IDirect3DCubeTexture9 *dt;
if (FAILED(IDirect3DDevice9_CreateCubeTexture(pD3DDev9, mips->mip[0].width, mips->mipcount/6, 0, fmt, D3DPOOL_MANAGED, &dt, NULL)))
if (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)
{
IDirect3DTexture9_Release(dt);
return false; return false;
} dbt = (IDirect3DBaseTexture9*)dt;
IDirect3DTexture9_LockRect(dt, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD); for (i = 0; i < mips->mipcount; i++)
//can't do it in one go. pitch might contain padding or be upside down.
if (!mips->mip[i].data)
;
else if (blocksize)
{ {
if (lock.Pitch == ((mips->mip[i].width+3)/4)*blocksize) IDirect3DCubeTexture9_GetLevelDesc(dt, i/6, &desc);
//for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(lock.pBits, mips->mip[i].data, mips->mip[i].datasize); if (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)
}
else if (swap)
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*4)
{ {
for (x = 0; x < mips->mip[i].width*4; x+=4) IDirect3DCubeTexture9_Release(dt);
return false;
}
IDirect3DCubeTexture9_LockRect(dt, i%6, i/6, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD);
//can't do it in one go. pitch might contain padding or be upside down.
if (!mips->mip[i].data)
;
else if (blocksize)
{
if (lock.Pitch == ((mips->mip[i].width+3)/4)*blocksize)
//for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(lock.pBits, mips->mip[i].data, mips->mip[i].datasize);
}
else if (swap)
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*4)
{ {
out[x+0] = in[x+2]; for (x = 0; x < mips->mip[i].width*4; x+=4)
out[x+1] = in[x+1]; {
out[x+2] = in[x+0]; out[x+0] = in[x+2];
out[x+3] = in[x+3]; out[x+1] = in[x+1];
out[x+2] = in[x+0];
out[x+3] = in[x+3];
}
} }
} }
else
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(out, in, mips->mip[i].width*pixelsize);
}
IDirect3DCubeTexture9_UnlockRect(dt, i%6, i/6);
} }
else
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(out, in, mips->mip[i].width*pixelsize);
}
IDirect3DTexture9_UnlockRect(dt, i);
} }
else
{
IDirect3DTexture9 *dt;
if (FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, mips->mip[0].width, mips->mip[0].height, mips->mipcount, 0, fmt, D3DPOOL_MANAGED, &dt, NULL)))
return false;
dbt = (IDirect3DBaseTexture9*)dt;
for (i = 0; i < mips->mipcount; i++)
{
IDirect3DTexture9_GetLevelDesc(dt, i, &desc);
if (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)
{
IDirect3DTexture9_Release(dt);
return false;
}
IDirect3DTexture9_LockRect(dt, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD);
//can't do it in one go. pitch might contain padding or be upside down.
if (!mips->mip[i].data)
;
else if (blocksize)
{
if (lock.Pitch == ((mips->mip[i].width+3)/4)*blocksize)
//for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(lock.pBits, mips->mip[i].data, mips->mip[i].datasize);
}
else if (swap)
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*4)
{
for (x = 0; x < mips->mip[i].width*4; x+=4)
{
out[x+0] = in[x+2];
out[x+1] = in[x+1];
out[x+2] = in[x+0];
out[x+3] = in[x+3];
}
}
}
else
{
for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize)
memcpy(out, in, mips->mip[i].width*pixelsize);
}
IDirect3DTexture9_UnlockRect(dt, i);
}
}
D3D9_DestroyTexture(tex); D3D9_DestroyTexture(tex);
tex->ptr = dt; tex->ptr = dbt;
return true; return true;
} }

View file

@ -474,6 +474,7 @@ void D3D9Shader_Init(void)
sh_config.env_add = 1; sh_config.env_add = 1;
sh_config.can_mipcap = true; //at creation time, I think. sh_config.can_mipcap = true; //at creation time, I think.
sh_config.havecubemaps = true;
IDirect3DDevice9_GetDeviceCaps(pD3DDev9, &caps); IDirect3DDevice9_GetDeviceCaps(pD3DDev9, &caps);

View file

@ -576,7 +576,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e
skinfile_t *sk = Mod_LookupSkin(e->customskin); skinfile_t *sk = Mod_LookupSkin(e->customskin);
if (sk) if (sk)
{ {
int i; int i, fallback=-1;
if (inf->geomset < MAX_GEOMSETS && sk->geomset[inf->geomset] != inf->geomid) if (inf->geomset < MAX_GEOMSETS && sk->geomset[inf->geomset] != inf->geomid)
return NULL; //don't allow this surface to be drawn. return NULL; //don't allow this surface to be drawn.
for (i = 0; i < sk->nummappings; i++) for (i = 0; i < sk->nummappings; i++)
@ -586,6 +586,13 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e
*forcedtex = &sk->mappings[i].texnums; *forcedtex = &sk->mappings[i].texnums;
return sk->mappings[i].shader; return sk->mappings[i].shader;
} }
if (!*sk->mappings[i].surface)
fallback = i;
}
if (fallback >= 0)
{
*forcedtex = &sk->mappings[fallback].texnums;
return sk->mappings[fallback].shader;
} }
if (!sk->qwskin && *sk->qwskinname) if (!sk->qwskin && *sk->qwskinname)
sk->qwskin = Skin_Lookup(sk->qwskinname); sk->qwskin = Skin_Lookup(sk->qwskinname);

View file

@ -1194,7 +1194,12 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass)
GL_LazyBind(tmu, GL_TEXTURE_CUBE_MAP_ARB, t); GL_LazyBind(tmu, GL_TEXTURE_CUBE_MAP_ARB, t);
return; return;
case T_GEN_REFLECTMASK: case T_GEN_REFLECTMASK:
t = shaderstate.curtexnums->reflectmask; if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->reflectmask))
t = shaderstate.curtexnums->reflectmask;
else if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->base))
t = shaderstate.curtexnums->base;
else
t = r_whiteimage;
break; break;
case T_GEN_SHADOWMAP: case T_GEN_SHADOWMAP:
t = shaderstate.curshadowmap; t = shaderstate.curshadowmap;
@ -1649,6 +1654,28 @@ static void GenerateTCFog(int passnum, mfog_t *fog)
} }
#endif #endif
static float *tcgen3(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)
{
int i;
vecV_t *src;
switch (pass->tcgen)
{
default:
case TC_GEN_SKYBOX:
src = mesh->xyz_array;
for (i = 0; i < cnt; i++, dst += 3)
{
dst[0] = src[i][0] - r_refdef.vieworg[0];
dst[1] = r_refdef.vieworg[1] - src[i][1];
dst[2] = src[i][2] - r_refdef.vieworg[2];
}
return dst-cnt*3;
// case TC_GEN_WOBBLESKY:
// case TC_GEN_REFLECT:
// break;
}
}
static float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh) static float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)
{ {
int i; int i;
@ -1684,7 +1711,7 @@ static float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t
dst[0] = DotProduct(pass->tcgenvec[0], src[i]); dst[0] = DotProduct(pass->tcgenvec[0], src[i]);
dst[1] = DotProduct(pass->tcgenvec[1], src[i]); dst[1] = DotProduct(pass->tcgenvec[1], src[i]);
} }
return dst; return dst-cnt*2;
// case TC_GEN_SKYBOX: // case TC_GEN_SKYBOX:
// case TC_GEN_WOBBLESKY: // case TC_GEN_WOBBLESKY:
@ -1772,11 +1799,75 @@ static void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, c
} }
break; break;
case SHADER_TCMOD_PAGE:
{ //simple atlas bias with no scaling. use a separate tcmod for that.
int w = tcmod->args[0];
int h = tcmod->args[1];
float f = shaderstate.curtime / (tcmod->args[2]*w*h);
int idx = (f - floor(f))*w*h;
t1 = (idx%w)/tcmod->args[0];
t2 = (idx/w)/tcmod->args[1];
for (j = 0; j < cnt; j++, dst += 2, src+=2)
{
dst[0] = src[0] + t1;
dst[1] = src[1] + t2;
}
}
break;
default: default:
break; break;
} }
} }
static void GenerateTCMods3(const shaderpass_t *pass, int passnum)
{
#if 1
int m;
float *src;
mesh_t *mesh;
for (m = 0; m < shaderstate.meshcount; m++)
{
mesh = shaderstate.meshes[m];
src = tcgen3(pass, mesh->numvertexes, texcoordarray[passnum]+mesh->vbofirstvert*3, mesh);
if (src != texcoordarray[passnum]+mesh->vbofirstvert*3)
{
//this shouldn't actually ever be true
memcpy(texcoordarray[passnum]+mesh->vbofirstvert*3, src, 8*mesh->numvertexes);
}
}
shaderstate.pendingtexcoordparts[passnum] = 3;
shaderstate.pendingtexcoordvbo[passnum] = 0;
shaderstate.pendingtexcoordpointer[passnum] = texcoordarray[passnum];
#else
GL_DeselectVAO();
if (!shaderstate.vbo_texcoords[passnum])
{
shaderstate.vbo_texcoords[passnum] = 0;
qglGenBuffersARB(1, &shaderstate.vbo_texcoords[passnum]);
}
GL_SelectVBO(shaderstate.vbo_texcoords[passnum]);
{
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, MAX_ARRAY_VERTS*sizeof(float)*3, NULL, GL_STREAM_DRAW_ARB);
for (; meshlist; meshlist = meshlist->next)
{
int i;
float *src;
src = tcge3n(pass, meshlist->numvertexes, texcoordarray[passnum], meshlist);
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, meshlist->vbofirstvert*8, meshlist->numvertexes*8, src);
}
}
shaderstate.pendingtexcoordparts[passnum] = 2;
shaderstate.pendingtexcoordvbo[passnum] = shaderstate.vbo_texcoords[passnum];
shaderstate.pendingtexcoordpointer[passnum] = NULL;
#endif
}
static void GenerateTCMods(const shaderpass_t *pass, int passnum) static void GenerateTCMods(const shaderpass_t *pass, int passnum)
{ {
#if 1 #if 1
@ -2565,7 +2656,9 @@ static void BE_GeneratePassTC(const shaderpass_t *pass, int tmu)
shaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->tvector.gl.vbo; shaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->tvector.gl.vbo;
shaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->tvector.gl.addr; shaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->tvector.gl.addr;
break; break;
// case TC_GEN_SKYBOX: case TC_GEN_SKYBOX:
GenerateTCMods3(pass, tmu);
break;
//position - viewpos //position - viewpos
// case TC_GEN_WOBBLESKY: // case TC_GEN_WOBBLESKY:
// case TC_GEN_REFLECT: // case TC_GEN_REFLECT:
@ -4674,6 +4767,7 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)
{ {
batch_t *batch, *masklists[2]; batch_t *batch, *masklists[2];
int i; int i;
float il;
if (!dynamiclist && !worldlist[SHADER_SORT_PORTAL]) if (!dynamiclist && !worldlist[SHADER_SORT_PORTAL])
return; //no portals to draw return; //no portals to draw
@ -4691,9 +4785,11 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)
if (batch->buildmeshes) if (batch->buildmeshes)
batch->buildmeshes(batch); batch->buildmeshes(batch);
il = shaderstate.identitylighting;
masklists[0] = worldlist[SHADER_SORT_PORTAL]; masklists[0] = worldlist[SHADER_SORT_PORTAL];
masklists[1] = dynamiclist; masklists[1] = dynamiclist;
GLR_DrawPortal(batch, worldlist, masklists, 0); GLR_DrawPortal(batch, worldlist, masklists, 0);
shaderstate.identitylighting = il;
/*clear depth again*/ /*clear depth again*/
GL_ForceDepthWritable(); GL_ForceDepthWritable();
@ -4837,7 +4933,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist)
} }
if (bs->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH)) if (bs->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH))
{ {
if (r_refract_fboival) if (r_refract_fboival || (bs->flags&SHADER_HASPORTAL))
{ {
float renderscale = min(1, r_refractreflect_scale.value); float renderscale = min(1, r_refractreflect_scale.value);
vrect_t ovrect = r_refdef.vrect; vrect_t ovrect = r_refdef.vrect;
@ -4899,7 +4995,10 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist)
GL_ForceDepthWritable(); GL_ForceDepthWritable();
qglClearColor(0, 0, 0, 1); qglClearColor(0, 0, 0, 1);
qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme if (bs->flags&SHADER_HASPORTAL)
GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 0);
else
GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme
GLBE_FBO_Pop(oldfbo); GLBE_FBO_Pop(oldfbo);
r_refdef.vrect = ovrect; r_refdef.vrect = ovrect;
@ -5565,11 +5664,14 @@ void GLBE_DrawWorld (batch_t **worldbatches)
shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
shaderstate.identitylighting = 1; shaderstate.identitylighting = r_lightmap_scale.value;
shaderstate.identitylighting *= r_refdef.hdr_value; shaderstate.identitylighting *= r_refdef.hdr_value;
shaderstate.identitylightmap = shaderstate.identitylighting; shaderstate.identitylightmap = shaderstate.identitylighting;
// shaderstate.identitylightmap *= 1<<gl_overbright.ival; // shaderstate.identitylightmap *= 1<<gl_overbright.ival;
// if (cl.worldmodel && cl.worldmodel->fromgame == fg_quake3)
// shaderstate.identitylighting *= 2;
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (r_lightprepass) if (r_lightprepass)
{ {
@ -5585,7 +5687,7 @@ void GLBE_DrawWorld (batch_t **worldbatches)
RSpeedRemark(); RSpeedRemark();
GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_OPAQUE);
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (worldbatches) if (worldbatches)
@ -5594,7 +5696,7 @@ void GLBE_DrawWorld (batch_t **worldbatches)
TRACE(("GLBE_DrawWorld: drawing lights\n")); TRACE(("GLBE_DrawWorld: drawing lights\n"));
GLBE_SelectEntity(&r_worldentity); GLBE_SelectEntity(&r_worldentity);
Sh_DrawLights(r_refdef.scenevis); Sh_DrawLights(r_refdef.scenevis);
RSpeedEnd(RSPEED_STENCILSHADOWS); RSpeedEnd(RSPEED_RTLIGHTS);
TRACE(("GLBE_DrawWorld: lights drawn\n")); TRACE(("GLBE_DrawWorld: lights drawn\n"));
} }
#endif #endif
@ -5602,7 +5704,9 @@ void GLBE_DrawWorld (batch_t **worldbatches)
shaderstate.identitylighting = 1; shaderstate.identitylighting = 1;
RSpeedRemark();
GLBE_SubmitMeshes(worldbatches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(worldbatches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_NEAREST);
RSpeedEnd(RSPEED_TRANSPARENTS);
/* if (r_refdef.gfog_alpha) /* if (r_refdef.gfog_alpha)
{ {

View file

@ -268,6 +268,8 @@ static struct font_s *curfont;
static float curfont_scale[2]; static float curfont_scale[2];
static qboolean curfont_scaled; static qboolean curfont_scaled;
extern cvar_t r_font_linear;
//^Ue2XX //^Ue2XX
static struct static struct
@ -359,10 +361,10 @@ void Font_Init(void)
for (i = 0; i < FONTPLANES; i++) for (i = 0; i < FONTPLANES; i++)
{ {
TEXASSIGN(fontplanes.texnum[i], Image_CreateTexture("***fontplane***", NULL, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA)); TEXASSIGN(fontplanes.texnum[i], Image_CreateTexture("***fontplane***", NULL, IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA));
} }
fontplanes.shader = R_RegisterShader("ftefont", SUF_NONE, fontplanes.shader = R_RegisterShader("ftefont", SUF_2D,
"{\n" "{\n"
"if $nofixed\n" "if $nofixed\n"
"program default2d\n" "program default2d\n"
@ -377,7 +379,7 @@ void Font_Init(void)
"}\n" "}\n"
); );
fontplanes.backshader = R_RegisterShader("ftefontback", SUF_NONE, fontplanes.backshader = R_RegisterShader("ftefontback", SUF_2D,
"{\n" "{\n"
"nomipmaps\n" "nomipmaps\n"
"{\n" "{\n"
@ -498,15 +500,20 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, in
int x, y; int x, y;
unsigned char *out; unsigned char *out;
struct charcache_s *c = &f->chars[charidx]; struct charcache_s *c = &f->chars[charidx];
int pad = 0;
#define BORDERCOLOUR 0
if (fontplanes.texnum[0]->flags & IF_LINEAR)
pad += 1; //pad the image data to avoid sampling outside
if (fontplanes.planerowx + (int)bmw >= PLANEWIDTH) if (fontplanes.planerowx + (int)bmw+pad*2 >= PLANEWIDTH)
{ {
fontplanes.planerowx = 0; fontplanes.planerowx = 0;
fontplanes.planerowy += fontplanes.planerowh; fontplanes.planerowy += fontplanes.planerowh;
fontplanes.planerowh = 0; fontplanes.planerowh = 0;
} }
if (fontplanes.planerowy+(int)bmh >= PLANEHEIGHT) if (fontplanes.planerowy+(int)bmh+pad*2 >= PLANEHEIGHT)
Font_FlushPlane(); Font_FlushPlane();
if (fontplanes.newestchar) if (fontplanes.newestchar)
@ -517,41 +524,71 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, in
c->nextchar = NULL; c->nextchar = NULL;
c->texplane = fontplanes.activeplane; c->texplane = fontplanes.activeplane;
c->bmx = fontplanes.planerowx; c->bmx = fontplanes.planerowx+pad;
c->bmy = fontplanes.planerowy; c->bmy = fontplanes.planerowy+pad;
c->bmw = bmw; c->bmw = bmw;
c->bmh = bmh; c->bmh = bmh;
if (fontplanes.planerowh < (int)bmh) if (fontplanes.planerowh < (int)bmh+pad*2)
fontplanes.planerowh = bmh; fontplanes.planerowh = bmh+pad*2;
fontplanes.planerowx += bmw; fontplanes.planerowx += bmw+pad*2;
out = (unsigned char *)&fontplanes.plane[c->bmx+(int)c->bmy*PLANEHEIGHT]; out = (unsigned char *)&fontplanes.plane[c->bmx+((int)c->bmy-pad)*PLANEHEIGHT];
if (alphaonly) if (alphaonly)
{ {
for (y = 0; y < bmh; y++) for (y = -pad; y < 0; y++)
{ {
for (x = 0; x < bmw; x++) for (x = -pad; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
for (; y < bmh; y++)
{
for (x = -pad; x < 0; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
for (; x < bmw; x++)
{ {
*(unsigned int *)&out[x*4] = 0xffffffff; *(unsigned int *)&out[x*4] = 0xffffffff;
out[x*4+3] = ((unsigned char*)data)[x]; out[x*4+3] = ((unsigned char*)data)[x];
} }
for (; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
data = (char*)data + pitch; data = (char*)data + pitch;
out += PLANEWIDTH*4; out += PLANEWIDTH*4;
} }
for (; y < bmh+pad; y++)
{
for (x = -pad; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
} }
else else
{ {
pitch*=4; pitch*=4;
for (y = 0; y < bmh; y++) for (y = -pad; y < 0; y++)
{ {
for (x = 0; x < bmw; x++) for (x = -pad; x < bmw+pad; x++)
{ *(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
for (; y < bmh; y++)
{
for (x = -pad; x < 0; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
for (; x < bmw; x++)
((unsigned int*)out)[x] = ((unsigned int*)data)[x]; ((unsigned int*)out)[x] = ((unsigned int*)data)[x];
} for (; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
data = (char*)data + pitch; data = (char*)data + pitch;
out += PLANEWIDTH*4; out += PLANEWIDTH*4;
} }
for (; y < bmh+pad; y++)
{
for (x = -pad; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
} }
fontplanes.planechanged = true; fontplanes.planechanged = true;
return c; return c;
@ -987,6 +1024,7 @@ static texid_t Font_LoadQuakeConchars(void)
return r_nulltex; return r_nulltex;
} }
#ifdef HEXEN2
static texid_t Font_LoadHexen2Conchars(qboolean iso88591) static texid_t Font_LoadHexen2Conchars(qboolean iso88591)
{ {
//gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system. //gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system.
@ -1074,6 +1112,7 @@ static texid_t Font_LoadHexen2Conchars(qboolean iso88591)
} }
return r_nulltex; return r_nulltex;
} }
#endif
FTE_ALIGN(4) qbyte default_conchar[/*11356*/] = FTE_ALIGN(4) qbyte default_conchar[/*11356*/] =
{ {
@ -1328,6 +1367,7 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
} }
} }
#endif #endif
#ifdef HEXEN2
if (!strcmp(fontfilename, "gfx/tinyfont")) if (!strcmp(fontfilename, "gfx/tinyfont"))
{ {
unsigned int *img; unsigned int *img;
@ -1387,6 +1427,7 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
} }
return f; return f;
} }
#endif
if (aname) if (aname)
{ {
@ -1469,11 +1510,13 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
if (!TEXLOADED(fontplanes.defaultfont)) if (!TEXLOADED(fontplanes.defaultfont))
fontplanes.defaultfont = Font_LoadDefaultConchars(); fontplanes.defaultfont = Font_LoadDefaultConchars();
#ifdef HEXEN2
if (!strcmp(fontfilename, "gfx/hexen2")) if (!strcmp(fontfilename, "gfx/hexen2"))
{ {
f->singletexture = Font_LoadHexen2Conchars(false); f->singletexture = Font_LoadHexen2Conchars(false);
defaultplane = DEFAULTPLANE; defaultplane = DEFAULTPLANE;
} }
#endif
if (!TEXLOADED(f->singletexture)) if (!TEXLOADED(f->singletexture))
f->singletexture = fontplanes.defaultfont; f->singletexture = fontplanes.defaultfont;
} }

View file

@ -5334,7 +5334,11 @@ void Terr_FinishTerrain(model_t *mod)
if (qrenderer != QR_NONE) if (qrenderer != QR_NONE)
{ {
if (*hm->skyname) if (*hm->skyname)
{
hm->skyshader = R_RegisterCustom(va("skybox_%s", hm->skyname), SUF_NONE, Shader_DefaultSkybox, NULL); hm->skyshader = R_RegisterCustom(va("skybox_%s", hm->skyname), SUF_NONE, Shader_DefaultSkybox, NULL);
if (!hm->skyshader->skydome)
hm->skyshader = NULL;
}
else else
hm->skyshader = NULL; hm->skyshader = NULL;

View file

@ -47,7 +47,6 @@ texture_t *r_notexture_mip = &r_notexture_mip_real;
#endif #endif
void CM_Init(void); void CM_Init(void);
void CM_Shutdown(void);
void Mod_LoadSpriteShaders(model_t *spr); void Mod_LoadSpriteShaders(model_t *spr);
qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize); qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize);
@ -821,9 +820,6 @@ void Mod_Shutdown (qboolean final)
Mod_Purge(MP_RESET); Mod_Purge(MP_RESET);
Mod_UnRegisterAllModelFormats(NULL); Mod_UnRegisterAllModelFormats(NULL);
#ifdef Q2BSPS
CM_Shutdown();
#endif
} }
else else
{ {
@ -1529,36 +1525,29 @@ static void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safe
if (!safetoloadfromwads) if (!safetoloadfromwads)
{ {
//remap to avoid bugging out on textures with the same name and different images (vanilla content sucks)
shadername = Mod_RemapBuggyTexture(shadername, tx->mips[0], tx->width*tx->height);
if (shadername)
origname = tx->name;
else
shadername = tx->name;
/*skies? just replace with the override sky*/ //find the *
if (!strncmp(tx->name, "sky", 3) && *cl.skyname) if (!*gl_shadeq1_name.string || !strcmp(gl_shadeq1_name.string, "*"))
tx->shader = R_RegisterCustom (va("skybox_%s", cl.skyname), SUF_NONE, Shader_DefaultSkybox, NULL); //just load the regular name. ;
else if (!(star = strchr(gl_shadeq1_name.string, '*')) || (strlen(gl_shadeq1_name.string)+strlen(tx->name)+1>=sizeof(altname))) //it's got to fit.
shadername = gl_shadeq1_name.string;
else else
{ {
//remap to avoid bugging out on textures with the same name and different images (vanilla content sucks) strncpy(altname, gl_shadeq1_name.string, star-gl_shadeq1_name.string); //copy the left
shadername = Mod_RemapBuggyTexture(shadername, tx->mips[0], tx->width*tx->height); altname[star-gl_shadeq1_name.string] = '\0';
if (shadername) strcat(altname, shadername); //insert the *
origname = tx->name; strcat(altname, star+1); //add any final text.
else shadername = altname;
shadername = tx->name;
//find the *
if (!*gl_shadeq1_name.string || !strcmp(gl_shadeq1_name.string, "*"))
;
else if (!(star = strchr(gl_shadeq1_name.string, '*')) || (strlen(gl_shadeq1_name.string)+strlen(tx->name)+1>=sizeof(altname))) //it's got to fit.
shadername = gl_shadeq1_name.string;
else
{
strncpy(altname, gl_shadeq1_name.string, star-gl_shadeq1_name.string); //copy the left
altname[star-gl_shadeq1_name.string] = '\0';
strcat(altname, shadername); //insert the *
strcat(altname, star+1); //add any final text.
shadername = altname;
}
tx->shader = R_RegisterCustom (shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);
} }
tx->shader = R_RegisterCustom (shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);
if (!tx->mips[0] && !safetoloadfromwads) if (!tx->mips[0] && !safetoloadfromwads)
return; return;
} }
@ -3979,11 +3968,21 @@ static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, i
for (j=0 ; j<2 ; j++) for (j=0 ; j<2 ; j++)
{ {
p = LittleShort (in->children[j]); p = (unsigned short)LittleShort (in->children[j]);
if (p >= 0)
if (p >= 0 && p < loadmodel->numnodes)
out->children[j] = loadmodel->nodes + p; out->children[j] = loadmodel->nodes + p;
else else
out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); {
p = (-1 - (signed)(0xffff0000|p));
if (p >= 0 && p < loadmodel->numleafs)
out->children[j] = (mnode_t *)(loadmodel->leafs + p);
else
{
Con_Printf (CON_ERROR "MOD_LoadBmodel: invalid node child %i in %s\n", LittleShort (in->children[j]), loadmodel->name);
return false;
}
}
} }
} }
} }
@ -4814,13 +4813,27 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b)
if (mod->fromgame == fg_quake3) if (mod->fromgame == fg_quake3)
{ {
for(a = 0; a < mod->numtexinfo; a++) if (mod->lightmaps.deluxemapping && mod->lightmaps.deluxemapping_modelspace)
{ {
mod->textures[a]->shader = R_RegisterShader_Lightmap(mod->textures[a]->name); for(a = 0; a < mod->numtexinfo; a++)
R_BuildDefaultTexnums(NULL, mod->textures[a]->shader); {
mod->textures[a]->shader = R_RegisterShader_Lightmap(va("%s#BUMPMODELSPACE", mod->textures[a]->name));
R_BuildDefaultTexnums(NULL, mod->textures[a]->shader);
mod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod->textures[a+mod->numtexinfo]->name); mod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod->textures[a+mod->numtexinfo]->name);
R_BuildDefaultTexnums(NULL, mod->textures[a+mod->numtexinfo]->shader); R_BuildDefaultTexnums(NULL, mod->textures[a+mod->numtexinfo]->shader);
}
}
else
{
for(a = 0; a < mod->numtexinfo; a++)
{
mod->textures[a]->shader = R_RegisterShader_Lightmap(mod->textures[a]->name);
R_BuildDefaultTexnums(NULL, mod->textures[a]->shader);
mod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod->textures[a+mod->numtexinfo]->name);
R_BuildDefaultTexnums(NULL, mod->textures[a+mod->numtexinfo]->shader);
}
} }
mod->textures[2*mod->numtexinfo]->shader = R_RegisterShader_Flare("noshader"); mod->textures[2*mod->numtexinfo]->shader = R_RegisterShader_Flare("noshader");
} }

View file

@ -997,6 +997,7 @@ typedef struct model_s
int height; //y size of lightmaps int height; //y size of lightmaps
int surfstyles; //numbers of style per surface. int surfstyles; //numbers of style per surface.
qboolean deluxemapping; //lightmaps are interleaved with deluxemap data (lightmap indicies should only be even values) qboolean deluxemapping; //lightmaps are interleaved with deluxemap data (lightmap indicies should only be even values)
qboolean deluxemapping_modelspace; //deluxemaps are in modelspace - we need different glsl.
} lightmaps; } lightmaps;
unsigned checksum; unsigned checksum;

View file

@ -42,6 +42,7 @@ sh_config_t sh_config;
//cvars that affect shader generation //cvars that affect shader generation
cvar_t r_vertexlight = CVARFD("r_vertexlight", "0", CVAR_SHADERSYSTEM, "Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering."); cvar_t r_vertexlight = CVARFD("r_vertexlight", "0", CVAR_SHADERSYSTEM, "Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering.");
cvar_t r_forceprogramify = CVARAFD("r_forceprogramify", "0", "dpcompat_makeshitup", CVAR_SHADERSYSTEM, "Reduce the shader to a single texture, and then make stuff up about its mother. The resulting fist fight results in more colour when you shine a light upon its face.\nSet to 2 to ignore 'depthfunc equal' and 'tcmod scale' in order to tolerate bizzare shaders made for a bizzare engine.\nBecause most shaders made for DP are by people who _clearly_ have no idea what the heck they're doing, you'll typically need the '2' setting."); cvar_t r_forceprogramify = CVARAFD("r_forceprogramify", "0", "dpcompat_makeshitup", CVAR_SHADERSYSTEM, "Reduce the shader to a single texture, and then make stuff up about its mother. The resulting fist fight results in more colour when you shine a light upon its face.\nSet to 2 to ignore 'depthfunc equal' and 'tcmod scale' in order to tolerate bizzare shaders made for a bizzare engine.\nBecause most shaders made for DP are by people who _clearly_ have no idea what the heck they're doing, you'll typically need the '2' setting.");
cvar_t dpcompat_nopremulpics = CVARFD("dpcompat_nopremulpics", "0", CVAR_SHADERSYSTEM, "By default FTE uses premultiplied alpha for hud/2d images, while DP does not (which results in halos with low-res content). Unfortunately DDS files would need to be recompressed, resulting in visible issues.");
extern cvar_t r_glsl_offsetmapping_reliefmapping; extern cvar_t r_glsl_offsetmapping_reliefmapping;
extern cvar_t r_fastturb, r_fastsky, r_skyboxname; extern cvar_t r_fastturb, r_fastsky, r_skyboxname;
extern cvar_t r_drawflat; extern cvar_t r_drawflat;
@ -234,6 +235,9 @@ static struct
vec3_t refractcolour; vec3_t refractcolour;
vec3_t reflectcolour; vec3_t reflectcolour;
float wateralpha; float wateralpha;
float specularexpscale; //*32 ish
float specularvalscale; //*1 ish
} parsestate; } parsestate;
typedef struct shaderkey_s typedef struct shaderkey_s
@ -552,17 +556,19 @@ static void Shader_ParseVector(shader_t *shader, char **ptr, vec3_t v)
} }
qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *images) qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *images)
{ { //FIXME: use Image_LoadCubemapTextureData to load the faces
//if possible directly use a 7th/cubemap texture instead
//this requires fixing the sky code to not do the random transforms thing though.
qboolean allokay = true; qboolean allokay = true;
int i, ss, sp; int i, ss, sp;
char path[MAX_QPATH]; char path[MAX_QPATH];
static char *skyname_suffix[][6] = { static char *skyname_suffix[][6] = {
{"rt", "bk", "lf", "ft", "up", "dn"}, {"rt", "bk", "lf", "ft", "up", "dn"},
{"px", "py", "nx", "ny", "pz", "nz"}, // {"px", "py", "nx", "ny", "pz", "nz"},
{"posx", "posy", "negx", "negy", "posz", "negz"}, // {"posx", "posy", "negx", "negy", "posz", "negz"},
{"_px", "_py", "_nx", "_ny", "_pz", "_nz"}, // {"_px", "_py", "_nx", "_ny", "_pz", "_nz"},
{"_posx", "_posy", "_negx", "_negy", "_posz", "_negz"}, // {"_posx", "_posy", "_negx", "_negy", "_posz", "_negz"},
{"_rt", "_bk", "_lf", "_ft", "_up", "_dn"} {"_rt", "_bk", "_lf", "_ft", "_up", "_dn"}
}; };
@ -588,6 +594,7 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima
if ( texturename[0] == '-' ) if ( texturename[0] == '-' )
{ {
images[i] = r_nulltex; images[i] = r_nulltex;
allokay = true;
} }
else else
{ {
@ -596,16 +603,14 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima
for (ss = 0; ss < sizeof(skyname_suffix)/sizeof(skyname_suffix[0]); ss++) for (ss = 0; ss < sizeof(skyname_suffix)/sizeof(skyname_suffix[0]); ss++)
{ {
Q_snprintfz ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] ); Q_snprintfz ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] );
images[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP|IF_NOWORKER); images[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP|IF_LOADNOW);
if (images[i]->status == TEX_LOADING) //FIXME: unsafe, as it can recurse through shader loading and mess up internal parse state. if (images[i]->width)
COM_WorkerPartialSync(images[i], &images[i]->status, TEX_LOADING);
if (TEXLOADED(images[i]))
break; break;
} }
if (TEXLOADED(images[i])) if (images[i]->width)
break; break;
} }
if (!TEXVALID(images[i])) if (!images[i]->width)
{ {
Con_Printf("Sky \"%s\" missing texture: %s\n", shadername, path); Con_Printf("Sky \"%s\" missing texture: %s\n", shadername, path);
images[i] = missing_texture; images[i] = missing_texture;
@ -916,7 +921,7 @@ static void Shader_SkyParms(shader_t *shader, shaderpass_t *pass, char **ptr)
/*skyheight =*/ Shader_ParseFloat(shader, ptr, 512); /*skyheight =*/ Shader_ParseFloat(shader, ptr, 512);
boxname = Shader_ParseString(ptr); boxname = Shader_ParseString(ptr);
Shader_ParseSkySides(shader->name, boxname, skydome->nearbox_textures); // Shader_ParseSkySides(shader->name, boxname, skydome->nearbox_textures);
shader->flags |= SHADER_SKY; shader->flags |= SHADER_SKY;
shader->sort = SHADER_SORT_SKY; shader->sort = SHADER_SORT_SKY;
@ -2320,6 +2325,7 @@ static void Shader_Translucent(shader_t *shader, shaderpass_t *pass, char **ptr)
static void Shader_DP_Camera(shader_t *shader, shaderpass_t *pass, char **ptr) static void Shader_DP_Camera(shader_t *shader, shaderpass_t *pass, char **ptr)
{ {
shader->sort = SHADER_SORT_PORTAL; shader->sort = SHADER_SORT_PORTAL;
parsestate.dpwatertype |= 4;
} }
static void Shader_DP_Water(shader_t *shader, shaderpass_t *pass, char **ptr) static void Shader_DP_Water(shader_t *shader, shaderpass_t *pass, char **ptr)
{ {
@ -2353,6 +2359,38 @@ static void Shader_DP_Refract(shader_t *shader, shaderpass_t *pass, char **ptr)
Shader_ParseVector(shader, ptr, parsestate.refractcolour); Shader_ParseVector(shader, ptr, parsestate.refractcolour);
} }
static void Shader_DP_OffsetMapping(shader_t *shader, shaderpass_t *pass, char **ptr)
{
char *token = Shader_ParseString(ptr);
if (!strcmp(token, "disable") || !strcmp(token, "none") || !strcmp(token, "off"))
;
else if (!strcmp(token, "default") || !strcmp(token, "normal"))
;
else if (!strcmp(token, "linear"))
;
else if (!strcmp(token, "relief"))
;
/*scale = */Shader_ParseFloat(shader, ptr, 1);
token = Shader_ParseString(ptr);
if (!strcmp(token, "bias"))
/*bias = */Shader_ParseFloat(shader, ptr, 0.5);
else if (!strcmp(token, "match"))
/*bias = 1.0 - */Shader_ParseFloat(shader, ptr, 0.5);
else if (!strcmp(token, "match8"))
/*bias = 1.0 - (1.0/255) * */Shader_ParseFloat(shader, ptr, 128);
else if (!strcmp(token, "match16"))
/*bias = 1.0 - (1.0/65535) * */Shader_ParseFloat(shader, ptr, 32768);
}
static void Shader_DP_GlossScale(shader_t *shader, shaderpass_t *pass, char **ptr)
{
parsestate.specularvalscale = Shader_ParseFloat(shader, ptr, 0);
}
static void Shader_DP_GlossExponent(shader_t *shader, shaderpass_t *pass, char **ptr)
{
parsestate.specularexpscale = Shader_ParseFloat(shader, ptr, 0);
}
static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr)
{ {
char subname[1024]; char subname[1024];
@ -2480,9 +2518,11 @@ static shaderkey_t shaderkeys[] =
{"water", Shader_DP_Water, "dp"}, {"water", Shader_DP_Water, "dp"},
{"reflect", Shader_DP_Reflect, "dp"}, {"reflect", Shader_DP_Reflect, "dp"},
{"refract", Shader_DP_Refract, "dp"}, {"refract", Shader_DP_Refract, "dp"},
{"offsetmapping", NULL, "dp"}, {"offsetmapping", Shader_DP_OffsetMapping, "dp"},
{"glossintensitymod",NULL, "dp"}, {"noshadow", NULL, "dp"},
{"glossexponentmod",NULL, "dp"}, {"polygonoffset", NULL, "dp"},
{"glossintensitymod",Shader_DP_GlossScale, "dp"}, //scales r_shadow_glossintensity(=1), aka: gl_specular
{"glossexponentmod",Shader_DP_GlossExponent, "dp"}, //scales r_shadow_glossexponent(=32)
/*doom3 compat*/ /*doom3 compat*/
{"diffusemap", Shader_DiffuseMap, "doom3"}, //macro for "{\nstage diffusemap\nmap <map>\n}" {"diffusemap", Shader_DiffuseMap, "doom3"}, //macro for "{\nstage diffusemap\nmap <map>\n}"
@ -2568,6 +2608,14 @@ static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *t
shader->flags |= SHADER_HASTOPBOTTOM; shader->flags |= SHADER_HASTOPBOTTOM;
pass->texgen = T_GEN_LOWEROVERLAY; pass->texgen = T_GEN_LOWEROVERLAY;
} }
else if (!Q_stricmp (tname, "$reflectcube"))
{
pass->texgen = T_GEN_REFLECTCUBE;
}
else if (!Q_stricmp (tname, "$reflectmask"))
{
pass->texgen = T_GEN_REFLECTMASK;
}
else if (!Q_stricmp (tname, "$shadowmap")) else if (!Q_stricmp (tname, "$shadowmap"))
{ {
pass->texgen = T_GEN_SHADOWMAP; pass->texgen = T_GEN_SHADOWMAP;
@ -3291,6 +3339,8 @@ static void Shaderpass_TcGen ( shader_t *shader, shaderpass_t *pass, char **ptr
pass->tcgen = TC_GEN_SVECTOR; pass->tcgen = TC_GEN_SVECTOR;
} else if ( !Q_stricmp (token, "tvector") ) { } else if ( !Q_stricmp (token, "tvector") ) {
pass->tcgen = TC_GEN_TVECTOR; pass->tcgen = TC_GEN_TVECTOR;
} else if ( !Q_stricmp (token, "skybox") ) {
pass->tcgen = TC_GEN_SKYBOX;
} }
} }
static void Shaderpass_EnvMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) //for alienarena static void Shaderpass_EnvMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) //for alienarena
@ -3784,6 +3834,8 @@ void Shader_Shutdown (void)
Shader_FlushCache(); Shader_FlushCache();
Shader_FlushGenerics(); Shader_FlushGenerics();
R_SkyShutdown();
r_maxshaders = 0; r_maxshaders = 0;
r_numshaders = 0; r_numshaders = 0;
@ -4292,13 +4344,13 @@ const char *Shader_AlphaMaskProgArgs(shader_t *s)
{ {
default: default:
break; break;
//cases inverted. the test is to enable //cases inverted. the atest is to retain the pixel, glsl is to discard (alpha OP MASK).
case SBITS_ATEST_GT0: case SBITS_ATEST_GT0:
return "#MASK=0.0#MASKOP=>"; return "#MASK=0.0#MASKLT=1";
case SBITS_ATEST_LT128: case SBITS_ATEST_LT128:
return "#MASK=0.5#MASKOP=<";
case SBITS_ATEST_GE128:
return "#MASK=0.5"; return "#MASK=0.5";
case SBITS_ATEST_GE128:
return "#MASK=0.5#MASKLT=1"; //ignore the eq part.
} }
} }
return ""; return "";
@ -4306,9 +4358,10 @@ const char *Shader_AlphaMaskProgArgs(shader_t *s)
void Shader_Programify (shader_t *s) void Shader_Programify (shader_t *s)
{ {
qboolean reflectrefract = false; unsigned int reflectrefract = 0;
char *prog = NULL; char *prog = NULL;
const char *mask; const char *mask;
char args[1024];
/* enum /* enum
{ {
T_UNKNOWN, T_UNKNOWN,
@ -4325,9 +4378,19 @@ void Shader_Programify (shader_t *s)
else if (pass->rgbgen == RGB_GEN_ENTITY) else if (pass->rgbgen == RGB_GEN_ENTITY)
modellighting = pass; modellighting = pass;
else if (pass->rgbgen == RGB_GEN_VERTEX_LIGHTING || pass->rgbgen == RGB_GEN_VERTEX_EXACT) else if (pass->rgbgen == RGB_GEN_VERTEX_LIGHTING || pass->rgbgen == RGB_GEN_VERTEX_EXACT)
vertexlighting = pass; {
if (s->usageflags & (SUF_LIGHTMAP|SUF_2D))
vertexlighting = pass;
else
modellighting = pass; //fucking DP morons who don't know what lightmaps are.
}
else if (pass->texgen == T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_LIGHTMAP) else if (pass->texgen == T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_LIGHTMAP)
lightmap = pass; {
if (s->usageflags & SUF_LIGHTMAP)
lightmap = pass;
else
modellighting = pass; //fucking DP morons who don't know what lightmaps are.
}
/*if (pass->numtcmods || (pass->shaderbits & SBITS_ATEST_BITS)) /*if (pass->numtcmods || (pass->shaderbits & SBITS_ATEST_BITS))
return; return;
@ -4360,7 +4423,16 @@ void Shader_Programify (shader_t *s)
s->passes[0].shaderbits &= ~(SBITS_BLEND_BITS|SBITS_MISC_NODEPTHTEST|SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY); s->passes[0].shaderbits &= ~(SBITS_BLEND_BITS|SBITS_MISC_NODEPTHTEST|SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY);
s->passes[0].shaderbits |= SBITS_MISC_DEPTHWRITE; s->passes[0].shaderbits |= SBITS_MISC_DEPTHWRITE;
reflectrefract = true; if (parsestate.dpwatertype & 1)
reflectrefract |= SHADER_HASREFLECT;
if (parsestate.dpwatertype & 2)
reflectrefract |= SHADER_HASREFRACT;
if (parsestate.dpwatertype & 4)
{
reflectrefract |= SHADER_HASREFRACT|SHADER_HASPORTAL; //doubles up as a 'camera'
if (s->sort == SHADER_SORT_PORTAL)
s->sort = SHADER_SORT_OPAQUE; //don't do it twice.
}
} }
else if (modellighting) else if (modellighting)
{ {
@ -4384,9 +4456,19 @@ void Shader_Programify (shader_t *s)
return; return;
} }
args[0] = 0;
if (parsestate.specularvalscale != 1)
Q_strncatz(args, va("#specmul=%g", parsestate.specularvalscale), sizeof(args));
if (parsestate.specularexpscale != 1)
Q_strncatz(args, va("#specexp=%g", parsestate.specularexpscale), sizeof(args));
mask = strchr(s->name, '#');
if (mask)
Q_strncatz(args, mask, sizeof(args));
mask = Shader_AlphaMaskProgArgs(s); mask = Shader_AlphaMaskProgArgs(s);
s->prog = Shader_FindGeneric(va("%s%s", prog, mask), qrenderer); s->prog = Shader_FindGeneric(va("%s%s%s", prog, mask, args), qrenderer);
if (s->prog) if (s->prog)
{ {
s->numpasses = 0; s->numpasses = 0;
@ -4401,8 +4483,7 @@ void Shader_Programify (shader_t *s)
s->passes[s->numpasses++].texgen = T_GEN_REFLECTION; s->passes[s->numpasses++].texgen = T_GEN_REFLECTION;
// s->passes[s->numpasses++].texgen = T_GEN_RIPPLEMAP; // s->passes[s->numpasses++].texgen = T_GEN_RIPPLEMAP;
// s->passes[s->numpasses++].texgen = T_GEN_REFRACTIONDEPTH; // s->passes[s->numpasses++].texgen = T_GEN_REFRACTIONDEPTH;
s->flags |= SHADER_HASREFRACT; s->flags |= reflectrefract;
s->flags |= SHADER_HASREFLECT;
} }
else else
{ {
@ -4422,6 +4503,7 @@ void Shader_Finish (shader_t *s)
//then the ambient stages. //then the ambient stages.
//and forget about the bump/specular stages as we don't support them and already stripped them. //and forget about the bump/specular stages as we don't support them and already stripped them.
#if 0
if (s->flags & SHADER_SKY) if (s->flags & SHADER_SKY)
{ {
/*skies go all black if fastsky is set*/ /*skies go all black if fastsky is set*/
@ -4449,6 +4531,7 @@ void Shader_Finish (shader_t *s)
return; return;
} }
} }
#endif
if (s->prog && !s->numpasses) if (s->prog && !s->numpasses)
{ {
@ -4809,7 +4892,6 @@ done:;
if (!s->prog && sh_config.progs_supported && (r_forceprogramify.ival || parsestate.forceprogramify)) if (!s->prog && sh_config.progs_supported && (r_forceprogramify.ival || parsestate.forceprogramify))
{ {
Shader_Programify(s);
if (r_forceprogramify.ival >= 2) if (r_forceprogramify.ival >= 2)
{ {
if (s->passes[0].numtcmods == 1 && s->passes[0].tcmods[0].type == SHADER_TCMOD_SCALE) if (s->passes[0].numtcmods == 1 && s->passes[0].tcmods[0].type == SHADER_TCMOD_SCALE)
@ -4818,6 +4900,7 @@ done:;
s->passes[0].shaderbits = (s->passes[0].shaderbits & ~SBITS_ATEST_BITS) | SBITS_ATEST_GE128; s->passes[0].shaderbits = (s->passes[0].shaderbits & ~SBITS_ATEST_BITS) | SBITS_ATEST_GE128;
s->passes[0].shaderbits &= ~SBITS_MISC_DEPTHEQUALONLY; //DP ignores this too. s->passes[0].shaderbits &= ~SBITS_MISC_DEPTHEQUALONLY; //DP ignores this too.
} }
Shader_Programify(s);
} }
if (s->prog) if (s->prog)
@ -5515,13 +5598,27 @@ void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *arg
/*shortname should begin with 'skybox_'*/ /*shortname should begin with 'skybox_'*/
void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args) void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args)
{ {
int i;
Shader_DefaultScript(shortname, s, Shader_DefaultScript(shortname, s,
va( va(
"{\n" "{\n"
"sort sky\n"
"surfaceparm nodlight\n"
"skyparms %s - -\n" "skyparms %s - -\n"
"}\n" "}\n"
, shortname+7) , shortname+7)
); );
for (i = 0; i < 6; i++)
{
if (s->skydome->farbox_textures[i] == missing_texture)
{
if (s->skydome)
Z_Free(s->skydome);
s->skydome = NULL;
return;
}
}
} }
char *Shader_DefaultBSPWater(shader_t *s, const char *shortname) char *Shader_DefaultBSPWater(shader_t *s, const char *shortname)
@ -5741,7 +5838,6 @@ void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args)
"{\n" "{\n"
"surfaceparm nodlight\n" "surfaceparm nodlight\n"
"skyparms - - -\n" "skyparms - - -\n"
"surfaceparm nodlight\n"
"}\n" "}\n"
); );
} }
@ -5812,7 +5908,7 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args)
if (!builtin && !strncmp(shortname, "sky", 3)) if (!builtin && !strncmp(shortname, "sky", 3))
{ {
//q1 sky //q1 sky
if (r_fastsky.ival) /*if (r_fastsky.ival)
{ {
builtin = ( builtin = (
"{\n" "{\n"
@ -5824,23 +5920,23 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args)
"surfaceparm nodlight\n" "surfaceparm nodlight\n"
"}\n" "}\n"
); );
} }*/
else if (*r_skyboxname.string || *cl.skyname) /*else if (*r_skyboxname.string || *cl.skyname)
{ {
builtin = ( qboolean okay;
"{\n" Z_Free(s->skydome);
"sort sky\n" s->skydome = (skydome_t *)Z_Malloc(sizeof(skydome_t));
"skyparms $r_skybox - -\n"
"surfaceparm nodlight\n" okay = Shader_ParseSkySides(shortname, "", s->skydome->farbox_textures);
"}\n" s->flags |= SHADER_SKY|SHADER_NODLIGHT;
); s->sort = SHADER_SORT_SKY;
Shader_DefaultScript(shortname, s, builtin);
if (s->flags & SHADER_SKY) if (okay)
return; return;
builtin = NULL; builtin = NULL;
/*if the r_skybox failed to load or whatever, reset and fall through and just use the regular sky*/ //if the r_skybox failed to load or whatever, reset and fall through and just use the regular sky
Shader_Reset(s); Shader_Reset(s);
} }*/
if (!builtin) if (!builtin)
builtin = ( builtin = (
"{\n" "{\n"
@ -6066,7 +6162,7 @@ void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs)
{ {
if (Shader_ParseShader("default2d", s)) if (Shader_ParseShader("default2d", s))
return; return;
if (sh_config.progs_supported && qrenderer != QR_DIRECT3D9) if (sh_config.progs_supported && qrenderer != QR_DIRECT3D9 && !dpcompat_nopremulpics.ival)
{ {
//hexen2 needs premultiplied alpha to avoid looking ugly //hexen2 needs premultiplied alpha to avoid looking ugly
//but that results in problems where things are drawn with alpha not 0, so scale vertex colour by alpha in the fragment program //but that results in problems where things are drawn with alpha not 0, so scale vertex colour by alpha in the fragment program
@ -6211,6 +6307,8 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, int parsemode)
memset(&parsestate, 0, sizeof(parsestate)); memset(&parsestate, 0, sizeof(parsestate));
parsestate.mode = parsemode; parsestate.mode = parsemode;
parsestate.specularexpscale = 1;
parsestate.specularvalscale = 1;
if (!s->defaulttextures) if (!s->defaulttextures)
{ {
@ -6432,11 +6530,161 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader
} }
#ifdef _DEBUG #ifdef _DEBUG
static char *Shader_DecomposePass(char *o, shaderpass_t *p, qboolean simple)
{
if (!simple)
{
switch(p->rgbgen)
{
case RGB_GEN_ENTITY: sprintf(o, "RGB_GEN_ENTITY "); break;
case RGB_GEN_ONE_MINUS_ENTITY: sprintf(o, "RGB_GEN_ONE_MINUS_ENTITY "); break;
case RGB_GEN_VERTEX_LIGHTING: sprintf(o, "RGB_GEN_VERTEX_LIGHTING "); break;
case RGB_GEN_VERTEX_EXACT: sprintf(o, "RGB_GEN_VERTEX_EXACT "); break;
case RGB_GEN_ONE_MINUS_VERTEX: sprintf(o, "RGB_GEN_ONE_MINUS_VERTEX "); break;
case RGB_GEN_IDENTITY_LIGHTING: sprintf(o, "RGB_GEN_IDENTITY_LIGHTING "); break;
case RGB_GEN_IDENTITY_OVERBRIGHT: sprintf(o, "RGB_GEN_IDENTITY_OVERBRIGHT "); break;
default:
case RGB_GEN_IDENTITY: sprintf(o, "RGB_GEN_IDENTITY "); break;
case RGB_GEN_CONST: sprintf(o, "RGB_GEN_CONST "); break;
case RGB_GEN_LIGHTING_DIFFUSE: sprintf(o, "RGB_GEN_LIGHTING_DIFFUSE "); break;
case RGB_GEN_WAVE: sprintf(o, "RGB_GEN_WAVE "); break;
case RGB_GEN_TOPCOLOR: sprintf(o, "RGB_GEN_TOPCOLOR "); break;
case RGB_GEN_BOTTOMCOLOR: sprintf(o, "RGB_GEN_BOTTOMCOLOR "); break;
}
o+=strlen(o);
sprintf(o, "\n"); o+=strlen(o);
}
if (p->shaderbits & SBITS_MISC_DEPTHWRITE) { sprintf(o, "SBITS_MISC_DEPTHWRITE\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_NODEPTHTEST) { sprintf(o, "SBITS_MISC_NODEPTHTEST\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_DEPTHEQUALONLY) { sprintf(o, "SBITS_MISC_DEPTHEQUALONLY\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_DEPTHCLOSERONLY) { sprintf(o, "SBITS_MISC_DEPTHCLOSERONLY\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_TESSELLATION) { sprintf(o, "SBITS_TESSELLATION\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_AFFINE) { sprintf(o, "SBITS_AFFINE\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MASK_BITS) { sprintf(o, "SBITS_MASK_BITS\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_BLEND_BITS)
{
sprintf(o, "blendfunc");
o+=strlen(o);
switch(p->shaderbits & SBITS_SRCBLEND_BITS)
{
case SBITS_SRCBLEND_NONE: sprintf(o, " SBITS_SRCBLEND_NONE"); break;
case SBITS_SRCBLEND_ZERO: sprintf(o, " SBITS_SRCBLEND_ZERO"); break;
case SBITS_SRCBLEND_ONE: sprintf(o, " SBITS_SRCBLEND_ONE"); break;
case SBITS_SRCBLEND_DST_COLOR: sprintf(o, " SBITS_SRCBLEND_DST_COLOR"); break;
case SBITS_SRCBLEND_ONE_MINUS_DST_COLOR: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_DST_COLOR"); break;
case SBITS_SRCBLEND_SRC_ALPHA: sprintf(o, " SBITS_SRCBLEND_SRC_ALPHA"); break;
case SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA"); break;
case SBITS_SRCBLEND_DST_ALPHA: sprintf(o, " SBITS_SRCBLEND_DST_ALPHA"); break;
case SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA"); break;
case SBITS_SRCBLEND_SRC_COLOR_INVALID: sprintf(o, " SBITS_SRCBLEND_SRC_COLOR_INVALID"); break;
case SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID"); break;
case SBITS_SRCBLEND_ALPHA_SATURATE: sprintf(o, " SBITS_SRCBLEND_ALPHA_SATURATE"); break;
default: sprintf(o, " SBITS_SRCBLEND_INVALID"); break;
}
o+=strlen(o);
switch(p->shaderbits & SBITS_DSTBLEND_BITS)
{
case SBITS_DSTBLEND_NONE: sprintf(o, " SBITS_DSTBLEND_NONE"); break;
case SBITS_DSTBLEND_ZERO: sprintf(o, " SBITS_DSTBLEND_ZERO"); break;
case SBITS_DSTBLEND_ONE: sprintf(o, " SBITS_DSTBLEND_ONE"); break;
case SBITS_DSTBLEND_DST_COLOR_INVALID: sprintf(o, " SBITS_DSTBLEND_DST_COLOR_INVALID"); break;
case SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID"); break;
case SBITS_DSTBLEND_SRC_ALPHA: sprintf(o, " SBITS_DSTBLEND_SRC_ALPHA"); break;
case SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA"); break;
case SBITS_DSTBLEND_DST_ALPHA: sprintf(o, " SBITS_DSTBLEND_DST_ALPHA"); break;
case SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA"); break;
case SBITS_DSTBLEND_SRC_COLOR: sprintf(o, " SBITS_DSTBLEND_SRC_COLOR"); break;
case SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR"); break;
case SBITS_DSTBLEND_ALPHA_SATURATE_INVALID: sprintf(o, " SBITS_DSTBLEND_ALPHA_SATURATE_INVALID"); break;
default: sprintf(o, " SBITS_DSTBLEND_INVALID"); break;
}
o+=strlen(o);
sprintf(o, "\n");
o+=strlen(o);
}
switch(p->shaderbits & SBITS_ATEST_BITS)
{
case SBITS_ATEST_GE128: sprintf(o, "SBITS_ATEST_GE128\n"); break;
case SBITS_ATEST_LT128: sprintf(o, "SBITS_ATEST_LT128\n"); break;
case SBITS_ATEST_GT0: sprintf(o, "SBITS_ATEST_GT0\n"); break;
}
o+=strlen(o);
return o;
}
static char *Shader_DecomposeSubPass(char *o, shaderpass_t *p, qboolean simple)
{
if (!simple)
{
switch(p->blendmode)
{
default:
case PBM_MODULATE: sprintf(o, "modulate "); break;
case PBM_OVERBRIGHT: sprintf(o, "overbright "); break;
case PBM_DECAL: sprintf(o, "decal "); break;
case PBM_ADD:sprintf(o, "add "); break;
case PBM_DOTPRODUCT: sprintf(o, "dotproduct "); break;
case PBM_REPLACE: sprintf(o, "replace "); break;
case PBM_REPLACELIGHT: sprintf(o, "replacelight "); break;
case PBM_MODULATE_PREV_COLOUR: sprintf(o, "modulate_prev "); break;
}
o+=strlen(o);
}
switch(p->texgen)
{
default:
case T_GEN_SINGLEMAP:
if (p->anim_frames[0])
{
sprintf(o, "singlemap \"%s\" %ix%i", p->anim_frames[0]->ident, p->anim_frames[0]->width, p->anim_frames[0]->height);
}
else
sprintf(o, "singlemap ");
break;
case T_GEN_ANIMMAP: sprintf(o, "animmap "); break;
case T_GEN_LIGHTMAP: sprintf(o, "lightmap "); break;
case T_GEN_DELUXMAP: sprintf(o, "deluxmap "); break;
case T_GEN_SHADOWMAP: sprintf(o, "shadowmap "); break;
case T_GEN_LIGHTCUBEMAP: sprintf(o, "lightcubemap "); break;
case T_GEN_DIFFUSE: sprintf(o, "diffuse "); break;
case T_GEN_NORMALMAP: sprintf(o, "normalmap "); break;
case T_GEN_SPECULAR: sprintf(o, "specular "); break;
case T_GEN_UPPEROVERLAY: sprintf(o, "upperoverlay "); break;
case T_GEN_LOWEROVERLAY: sprintf(o, "loweroverlay "); break;
case T_GEN_FULLBRIGHT: sprintf(o, "fullbright "); break;
case T_GEN_PALETTED: sprintf(o, "paletted "); break;
case T_GEN_REFLECTCUBE: sprintf(o, "reflectcube "); break;
case T_GEN_REFLECTMASK: sprintf(o, "reflectmask "); break;
case T_GEN_CURRENTRENDER: sprintf(o, "currentrender "); break;
case T_GEN_SOURCECOLOUR: sprintf(o, "sourcecolour "); break;
case T_GEN_SOURCEDEPTH: sprintf(o, "sourcedepth "); break;
case T_GEN_REFLECTION: sprintf(o, "reflection "); break;
case T_GEN_REFRACTION: sprintf(o, "refraction "); break;
case T_GEN_REFRACTIONDEPTH: sprintf(o, "refractiondepth "); break;
case T_GEN_RIPPLEMAP: sprintf(o, "ripplemap "); break;
case T_GEN_SOURCECUBE: sprintf(o, "sourcecube "); break;
case T_GEN_VIDEOMAP: sprintf(o, "videomap "); break;
case T_GEN_CUBEMAP: sprintf(o, "cubemap "); break;
case T_GEN_3DMAP: sprintf(o, "3dmap "); break;
}
o+=strlen(o);
sprintf(o, "\n"); o+=strlen(o);
return o;
}
char *Shader_Decompose(shader_t *s) char *Shader_Decompose(shader_t *s)
{ {
static char decomposebuf[32768]; static char decomposebuf[32768];
char *o = decomposebuf; char *o = decomposebuf;
sprintf(o, "<---\n"); o+=strlen(o); shaderpass_t *p;
unsigned int i, j;
sprintf(o, "\n<---\n"); o+=strlen(o);
switch (s->sort) switch (s->sort)
{ {
@ -6461,153 +6709,22 @@ char *Shader_Decompose(shader_t *s)
{ {
sprintf(o, "program\n"); sprintf(o, "program\n");
o+=strlen(o); o+=strlen(o);
p = s->passes;
o = Shader_DecomposePass(o, p, true);
for (j = 0; j < s->numpasses; j++)
o = Shader_DecomposeSubPass(o, p+j, true);
} }
else else
{ {
unsigned int i, j;
shaderpass_t *p;
for (i = 0; i < s->numpasses; i+= p->numMergedPasses) for (i = 0; i < s->numpasses; i+= p->numMergedPasses)
{ {
p = &s->passes[i]; p = &s->passes[i];
sprintf(o, "{\n"); o+=strlen(o); sprintf(o, "{\n"); o+=strlen(o);
switch(p->rgbgen) o = Shader_DecomposePass(o, p, false);
{
case RGB_GEN_ENTITY: sprintf(o, "RGB_GEN_ENTITY "); break;
case RGB_GEN_ONE_MINUS_ENTITY: sprintf(o, "RGB_GEN_ONE_MINUS_ENTITY "); break;
case RGB_GEN_VERTEX_LIGHTING: sprintf(o, "RGB_GEN_VERTEX_LIGHTING "); break;
case RGB_GEN_VERTEX_EXACT: sprintf(o, "RGB_GEN_VERTEX_EXACT "); break;
case RGB_GEN_ONE_MINUS_VERTEX: sprintf(o, "RGB_GEN_ONE_MINUS_VERTEX "); break;
case RGB_GEN_IDENTITY_LIGHTING: sprintf(o, "RGB_GEN_IDENTITY_LIGHTING "); break;
case RGB_GEN_IDENTITY_OVERBRIGHT: sprintf(o, "RGB_GEN_IDENTITY_OVERBRIGHT "); break;
default:
case RGB_GEN_IDENTITY: sprintf(o, "RGB_GEN_IDENTITY "); break;
case RGB_GEN_CONST: sprintf(o, "RGB_GEN_CONST "); break;
case RGB_GEN_LIGHTING_DIFFUSE: sprintf(o, "RGB_GEN_LIGHTING_DIFFUSE "); break;
case RGB_GEN_WAVE: sprintf(o, "RGB_GEN_WAVE "); break;
case RGB_GEN_TOPCOLOR: sprintf(o, "RGB_GEN_TOPCOLOR "); break;
case RGB_GEN_BOTTOMCOLOR: sprintf(o, "RGB_GEN_BOTTOMCOLOR "); break;
}
o+=strlen(o);
sprintf(o, "\n"); o+=strlen(o);
if (p->shaderbits & SBITS_MISC_DEPTHWRITE) { sprintf(o, "SBITS_MISC_DEPTHWRITE\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_NODEPTHTEST) { sprintf(o, "SBITS_MISC_NODEPTHTEST\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_DEPTHEQUALONLY) { sprintf(o, "SBITS_MISC_DEPTHEQUALONLY\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MISC_DEPTHCLOSERONLY) { sprintf(o, "SBITS_MISC_DEPTHCLOSERONLY\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_TESSELLATION) { sprintf(o, "SBITS_TESSELLATION\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_AFFINE) { sprintf(o, "SBITS_AFFINE\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_MASK_BITS) { sprintf(o, "SBITS_MASK_BITS\n"); o+=strlen(o); }
if (p->shaderbits & SBITS_BLEND_BITS)
{
sprintf(o, "blendfunc");
o+=strlen(o);
switch(p->shaderbits & SBITS_SRCBLEND_BITS)
{
case SBITS_SRCBLEND_NONE: sprintf(o, " SBITS_SRCBLEND_NONE"); break;
case SBITS_SRCBLEND_ZERO: sprintf(o, " SBITS_SRCBLEND_ZERO"); break;
case SBITS_SRCBLEND_ONE: sprintf(o, " SBITS_SRCBLEND_ONE"); break;
case SBITS_SRCBLEND_DST_COLOR: sprintf(o, " SBITS_SRCBLEND_DST_COLOR"); break;
case SBITS_SRCBLEND_ONE_MINUS_DST_COLOR: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_DST_COLOR"); break;
case SBITS_SRCBLEND_SRC_ALPHA: sprintf(o, " SBITS_SRCBLEND_SRC_ALPHA"); break;
case SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA"); break;
case SBITS_SRCBLEND_DST_ALPHA: sprintf(o, " SBITS_SRCBLEND_DST_ALPHA"); break;
case SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA"); break;
case SBITS_SRCBLEND_SRC_COLOR_INVALID: sprintf(o, " SBITS_SRCBLEND_SRC_COLOR_INVALID"); break;
case SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID: sprintf(o, " SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID"); break;
case SBITS_SRCBLEND_ALPHA_SATURATE: sprintf(o, " SBITS_SRCBLEND_ALPHA_SATURATE"); break;
default: sprintf(o, " SBITS_SRCBLEND_INVALID"); break;
}
o+=strlen(o);
switch(p->shaderbits & SBITS_DSTBLEND_BITS)
{
case SBITS_DSTBLEND_NONE: sprintf(o, " SBITS_DSTBLEND_NONE"); break;
case SBITS_DSTBLEND_ZERO: sprintf(o, " SBITS_DSTBLEND_ZERO"); break;
case SBITS_DSTBLEND_ONE: sprintf(o, " SBITS_DSTBLEND_ONE"); break;
case SBITS_DSTBLEND_DST_COLOR_INVALID: sprintf(o, " SBITS_DSTBLEND_DST_COLOR_INVALID"); break;
case SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID"); break;
case SBITS_DSTBLEND_SRC_ALPHA: sprintf(o, " SBITS_DSTBLEND_SRC_ALPHA"); break;
case SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA"); break;
case SBITS_DSTBLEND_DST_ALPHA: sprintf(o, " SBITS_DSTBLEND_DST_ALPHA"); break;
case SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA"); break;
case SBITS_DSTBLEND_SRC_COLOR: sprintf(o, " SBITS_DSTBLEND_SRC_COLOR"); break;
case SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR: sprintf(o, " SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR"); break;
case SBITS_DSTBLEND_ALPHA_SATURATE_INVALID: sprintf(o, " SBITS_DSTBLEND_ALPHA_SATURATE_INVALID"); break;
default: sprintf(o, " SBITS_DSTBLEND_INVALID"); break;
}
o+=strlen(o);
sprintf(o, "\n");
o+=strlen(o);
}
switch(p->shaderbits & SBITS_ATEST_BITS)
{
case SBITS_ATEST_GE128: sprintf(o, "SBITS_ATEST_GE128\n"); break;
case SBITS_ATEST_LT128: sprintf(o, "SBITS_ATEST_LT128\n"); break;
case SBITS_ATEST_GT0: sprintf(o, "SBITS_ATEST_GT0\n"); break;
}
o+=strlen(o);
for (j = 0; j < p->numMergedPasses; j++) for (j = 0; j < p->numMergedPasses; j++)
{ o = Shader_DecomposeSubPass(o, p+j, false);
switch(p[j].blendmode)
{
default:
case PBM_MODULATE: sprintf(o, "modulate "); break;
case PBM_OVERBRIGHT: sprintf(o, "overbright "); break;
case PBM_DECAL: sprintf(o, "decal "); break;
case PBM_ADD:sprintf(o, "add "); break;
case PBM_DOTPRODUCT: sprintf(o, "dotproduct "); break;
case PBM_REPLACE: sprintf(o, "replace "); break;
case PBM_REPLACELIGHT: sprintf(o, "replacelight "); break;
case PBM_MODULATE_PREV_COLOUR: sprintf(o, "modulate_prev "); break;
}
o+=strlen(o);
switch(p[j].texgen)
{
default:
case T_GEN_SINGLEMAP:
if (p[j].anim_frames[0])
{
sprintf(o, "singlemap \"%s\" %ix%i", p[j].anim_frames[0]->ident, p[j].anim_frames[0]->width, p[j].anim_frames[0]->height);
}
else
sprintf(o, "singlemap ");
break;
case T_GEN_ANIMMAP: sprintf(o, "animmap "); break;
case T_GEN_LIGHTMAP: sprintf(o, "lightmap "); break;
case T_GEN_DELUXMAP: sprintf(o, "deluxmap "); break;
case T_GEN_SHADOWMAP: sprintf(o, "shadowmap "); break;
case T_GEN_LIGHTCUBEMAP: sprintf(o, "lightcubemap "); break;
case T_GEN_DIFFUSE: sprintf(o, "diffuse "); break;
case T_GEN_NORMALMAP: sprintf(o, "normalmap "); break;
case T_GEN_SPECULAR: sprintf(o, "specular "); break;
case T_GEN_UPPEROVERLAY: sprintf(o, "upperoverlay "); break;
case T_GEN_LOWEROVERLAY: sprintf(o, "loweroverlay "); break;
case T_GEN_FULLBRIGHT: sprintf(o, "fullbright "); break;
case T_GEN_PALETTED: sprintf(o, "paletted "); break;
case T_GEN_REFLECTCUBE: sprintf(o, "reflectcube "); break;
case T_GEN_REFLECTMASK: sprintf(o, "reflectmask "); break;
case T_GEN_CURRENTRENDER: sprintf(o, "currentrender "); break;
case T_GEN_SOURCECOLOUR: sprintf(o, "sourcecolour "); break;
case T_GEN_SOURCEDEPTH: sprintf(o, "sourcedepth "); break;
case T_GEN_REFLECTION: sprintf(o, "reflection "); break;
case T_GEN_REFRACTION: sprintf(o, "refraction "); break;
case T_GEN_REFRACTIONDEPTH: sprintf(o, "refractiondepth "); break;
case T_GEN_RIPPLEMAP: sprintf(o, "ripplemap "); break;
case T_GEN_SOURCECUBE: sprintf(o, "sourcecube "); break;
case T_GEN_VIDEOMAP: sprintf(o, "videomap "); break;
case T_GEN_CUBEMAP: sprintf(o, "cubemap "); break;
case T_GEN_3DMAP: sprintf(o, "3dmap "); break;
}
o+=strlen(o);
sprintf(o, "\n"); o+=strlen(o);
}
sprintf(o, "}\n"); o+=strlen(o); sprintf(o, "}\n"); o+=strlen(o);
} }
} }
@ -6618,7 +6735,7 @@ char *Shader_Decompose(shader_t *s)
char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize) char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize)
{ {
char *adr, *parsename=NULL; char *adr, *parsename=NULL, *argsstart;
char cleanname[MAX_QPATH]; char cleanname[MAX_QPATH];
char shortname[MAX_QPATH]; char shortname[MAX_QPATH];
char drivername[MAX_QPATH]; char drivername[MAX_QPATH];
@ -6632,8 +6749,11 @@ char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize)
saveshaderbody = &adr; saveshaderbody = &adr;
strcpy(cleanname, s->name); strcpy(cleanname, s->name);
argsstart = strchr(cleanname, '#');
if (argsstart)
*argsstart = 0;
COM_StripExtension (cleanname, shortname, sizeof(shortname)); COM_StripExtension (cleanname, shortname, sizeof(shortname));
if (ruleset_allow_shaders.ival) if (ruleset_allow_shaders.ival && !(s->usageflags & SUR_FORCEFALLBACK))
{ {
if (sh_config.shadernamefmt) if (sh_config.shadernamefmt)
{ {
@ -6777,7 +6897,7 @@ void Shader_DoReload(void)
shader_t *s; shader_t *s;
unsigned int i; unsigned int i;
char shortname[MAX_QPATH]; char shortname[MAX_QPATH];
char cleanname[MAX_QPATH]; char cleanname[MAX_QPATH], *argsstart;
int oldsort; int oldsort;
qboolean resort = false; qboolean resort = false;
@ -6821,8 +6941,11 @@ void Shader_DoReload(void)
continue; continue;
strcpy(cleanname, s->name); strcpy(cleanname, s->name);
argsstart = strchr(cleanname, '#');
if (argsstart)
*argsstart = 0;
COM_StripExtension (cleanname, shortname, sizeof(shortname)); COM_StripExtension (cleanname, shortname, sizeof(shortname));
if (ruleset_allow_shaders.ival) if (ruleset_allow_shaders.ival && !(s->usageflags & SUR_FORCEFALLBACK))
{ {
if (sh_config.shadernamefmt) if (sh_config.shadernamefmt)
{ {

View file

@ -1243,6 +1243,14 @@ static const char *glsl_hdrs[] =
"#ifndef USE_ARB_SHADOW\n" //fall back on regular samplers if we must "#ifndef USE_ARB_SHADOW\n" //fall back on regular samplers if we must
"#define sampler2DShadow sampler2D\n" "#define sampler2DShadow sampler2D\n"
"#endif\n" "#endif\n"
"#ifndef SPECEXP\n"
"#define SPECEXP 1.0\n"
"#endif\n"
"#define FTE_SPECULAR_EXPONENT (32.0*float(SPECEXP))\n"
"#ifndef SPECMUL\n"
"#define SPECMUL 1.0\n"
"#endif\n"
"#define FTE_SPECULAR_MULTIPLIER (cvar_gl_specular*float(SPECMUL))\n"
#ifndef NOLEGACY #ifndef NOLEGACY
"uniform sampler2DShadow s_shadowmap;" "uniform sampler2DShadow s_shadowmap;"
"uniform samplerCube s_projectionmap;" "uniform samplerCube s_projectionmap;"
@ -2854,12 +2862,16 @@ void GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
sh_config.shadernamefmt = "%s_gles"; sh_config.shadernamefmt = "%s_gles";
sh_config.can_mipcap = gl_config.glversion >= 3.0; sh_config.can_mipcap = gl_config.glversion >= 3.0;
sh_config.havecubemaps = gl_config.glversion >= 2.0;
} }
else else
{ {
GLint srgb = gl_config.glversion >= 2.1 || GL_CheckExtension("GL_EXT_texture_sRGB"); //became core in gl 2.1 GLint srgb = gl_config.glversion >= 2.1 || GL_CheckExtension("GL_EXT_texture_sRGB"); //became core in gl 2.1
sh_config.can_mipcap = gl_config.glversion >= 1.2; sh_config.can_mipcap = gl_config.glversion >= 1.2;
sh_config.havecubemaps = gl_config.glversion >= 1.3; //cubemaps AND clamp-to-edge.
sh_config.texfmt[PTI_RGBX8] = true; //proper support sh_config.texfmt[PTI_RGBX8] = true; //proper support
//these require stuff like GL_UNSIGNED_SHORT_5_5_5_1 etc, which needs gl 1.2+ //these require stuff like GL_UNSIGNED_SHORT_5_5_5_1 etc, which needs gl 1.2+

View file

@ -1403,29 +1403,38 @@ qboolean GLVID_ApplyGammaRamps(unsigned int rampcount, unsigned short *ramps)
if (!vm.originalapplied) if (!vm.originalapplied)
return false; return false;
if (ramps || rampcount != vm.originalrampsize) if (ramps && rampcount == vm.originalrampsize)
{ {
//hardwaregamma==1 skips hardware gamma when we're not fullscreen, in favour of software glsl based gamma. switch(vid_hardwaregamma.ival)
// if (vid_hardwaregamma.value == 1 && !vid.activeapp && !(fullscreenflags & FULLSCREEN_ACTIVE)) {
// return false; case 0: //never use hardware/glsl gamma
// if (!vid.activeapp) case 2: //ALWAYS use glsl gamma
// return false; return false;
// if (!vid_hardwaregamma.value) default:
// return false; case 1: //no hardware gamma when windowed
if (!(fullscreenflags & FULLSCREEN_ACTIVE))
//we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma if it randomly fails (yuck) return false;
if (gammaworks) break;
vm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]); case 3: //ALWAYS try to use hardware gamma, even when it fails...
else break;
gammaworks = !!vm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]); }
return gammaworks; if (vid.activeapp)
{
if (gammaworks)
vm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]);
else
gammaworks = !!vm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]);
return gammaworks;
}
return false;
} }
else else if (gammaworks)
{ {
vm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]); vm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);
return true; return true;
} }
return false;
} }
void GLVID_SwapBuffers (void) void GLVID_SwapBuffers (void)

View file

@ -2137,32 +2137,37 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
{ {
if (ramps) if (ramps)
{ {
if (!gammaworks || gammarampsize != 256) switch(vid_hardwaregamma.ival)
return false;
if (vid_hardwaregamma.value == 1 && modestate == MS_WINDOWED)
return false; //don't do hardware gamma in windowed mode
if (vid.activeapp && vid_hardwaregamma.value) //this is needed because ATI drivers don't work properly (or when task-switched out).
{ {
if (gammaworks) case 0: //never use hardware/glsl gamma
{ //we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma (yuck) case 2: //ALWAYS use glsl gamma
if (vid_desktopgamma.value) return false;
{ default:
HDC hDC = GetDC(GetDesktopWindow()); case 1: //no hardware gamma when windowed
qSetDeviceGammaRamp (hDC, ramps); if (modestate == MS_WINDOWED)
ReleaseDC(GetDesktopWindow(), hDC); return false;
} break;
else case 3: //ALWAYS try to use hardware gamma, even when it fails...
{ break;
qSetDeviceGammaRamp (maindc, ramps); }
}
if (vid.activeapp) //this is needed because ATI drivers don't work properly (or when task-switched out).
{ //we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma (yuck)
if (vid_desktopgamma.value)
{
HDC hDC = GetDC(GetDesktopWindow());
qSetDeviceGammaRamp (hDC, ramps);
ReleaseDC(GetDesktopWindow(), hDC);
}
else
{
qSetDeviceGammaRamp (maindc, ramps);
} }
return true; return true;
} }
return false; return false;
} }
else else if (gammaworks)
{ {
//revert to default //revert to default
if (qSetDeviceGammaRamp) if (qSetDeviceGammaRamp)
@ -2182,6 +2187,7 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
} }
return true; return true;
} }
return false;
} }
void GLVID_Crashed(void) void GLVID_Crashed(void)

View file

@ -333,21 +333,24 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
if (ramps && gammarampsize == 256) if (ramps && gammarampsize == 256)
{ {
if (vid_hardwaregamma.value) switch(vid_hardwaregamma.ival)
{ {
if (gammaworks) case 0: //never use hardware/glsl gamma
{ //we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma (yuck) case 2: //ALWAYS use glsl gamma
SDL_SetWindowGammaRamp (sdlwindow, &ramps[0], &ramps[256], &ramps[512]); return false;
return true; default:
} case 1: //no hardware gamma when windowed
gammaworks = !SDL_SetWindowGammaRamp (sdlwindow, &ramps[0], &ramps[256], &ramps[512]); if (!vid_isfullscreen)
return false;
break;
case 3: //ALWAYS try to use hardware gamma, even when it fails...
break;
} }
else
gammaworks = false;
gammaworks |= !SDL_SetWindowGammaRamp (sdlwindow, &ramps[0], &ramps[256], &ramps[512]);
return gammaworks; return gammaworks;
} }
else else if (gammaworks)
{ {
SDL_SetWindowGammaRamp (sdlwindow, NULL, NULL, NULL); SDL_SetWindowGammaRamp (sdlwindow, NULL, NULL, NULL);
return true; return true;
@ -355,18 +358,21 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
#else #else
if (ramps && gammarampsize == 256) if (ramps && gammarampsize == 256)
{ {
if (vid_hardwaregamma.value) switch(vid_hardwaregamma.ival)
{ {
if (gammaworks) case 0: //never use hardware/glsl gamma
{ //we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma (yuck) case 2: //ALWAYS use glsl gamma
SDL_SetGammaRamp (&ramps[0], &ramps[256], &ramps[512]); return false;
return true; default:
} case 1: //no hardware gamma when windowed
gammaworks = !SDL_SetGammaRamp (&ramps[0], &ramps[256], &ramps[512]); if (!vid_isfullscreen)
return false;
break;
case 3: //ALWAYS try to use hardware gamma, even when it fails...
break;
} }
else
gammaworks = false;
gammaworks |= !SDL_SetGammaRamp (&ramps[0], &ramps[256], &ramps[512]);
return gammaworks; return gammaworks;
} }
else else

View file

@ -36,7 +36,7 @@ extern cvar_t gl_skyboxdist;
extern cvar_t r_fastsky; extern cvar_t r_fastsky;
extern cvar_t r_fastskycolour; extern cvar_t r_fastskycolour;
static shader_t *forcedskyshader; static shader_t *forcedsky;
static shader_t *skyboxface; static shader_t *skyboxface;
static shader_t *skygridface; static shader_t *skygridface;
@ -44,47 +44,77 @@ static shader_t *skygridface;
//========================================================= //=========================================================
void R_SetSky(char *skyname) //called on video shutdown to reset internal state
void R_SkyShutdown(void)
{ {
extern cvar_t r_skyboxname; skyboxface = NULL;
forcedskyshader = NULL; skygridface = NULL;
forcedsky = NULL;
if (!qrenderer)
return;
if (*r_skyboxname.string) //user's setting overrides.
skyname = r_skyboxname.string;
Shader_NeedReload(false);
if (*skyname)
forcedskyshader = R_RegisterCustom(va("skybox_%s", skyname), SUF_NONE, Shader_DefaultSkybox, NULL);
skyboxface = R_RegisterShader("skyboxface", SUF_NONE,
"{\n"
"program default2d\n"
"{\n"
"map $diffuse\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"}\n"
);
skygridface = R_RegisterShader("skygridface", SUF_NONE,
"{\n"
"program default2d\n"
"{\n"
"map $diffuse\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"{\n"
"map $fullbright\n"
"blendfunc blend\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"}\n"
);
} }
void R_SetSky(const char *sky)
{
int i;
const char *shadername;
extern cvar_t r_skyboxname;
Q_strncpyz(cl.skyname, sky, sizeof(cl.skyname));
if (*r_skyboxname.string) //override it with the user's preference
sky = r_skyboxname.string;
shadername = va("skybox_%s", sky);
if (!forcedsky || strcmp(shadername, forcedsky->name))
{
forcedsky = NULL; //fall back to regular skies if forcing fails.
if (!*sky)
return; //no need to do anything
//if we have cubemaps then we can just go and use a cubemap for our skybox
if (sh_config.havecubemaps)
{
texnums_t tex;
memset(&tex, 0, sizeof(tex));
tex.reflectcube = R_LoadHiResTexture(sky, "env:gfx/env", IF_LOADNOW|IF_CUBEMAP|IF_CLAMP);
if (tex.reflectcube->width)
{
forcedsky = R_RegisterShader(va("skybox_%s", sky), 0, va("{\nsort sky\nprogram defaultskybox\n{\nmap \"$cube:$reflectcube\"\ntcgen skybox\n}\nsurfaceparms nodlight\nsurfaceparms sky\n}", sky));
R_BuildDefaultTexnums(&tex, forcedsky);
return;
}
}
//crappy old path that I still need to fix up a bit
//unlike cubemaps, this works on gl1.1/gles1, and also works with the different faces as different sizes.
forcedsky = R_RegisterShader(shadername, 0, va("{\nsort sky\nskyparms \"%s\" 512 -\nsurfaceparms nodlight\n}", sky));
//check that we actually got some textures.
//we accept the skybox if even 1 face is valid.
//we ignore the replacement only request if all are invalid.
for (i = 0; i < 6; i++)
{
extern texid_t missing_texture;
if (forcedsky->skydome && forcedsky->skydome->farbox_textures[i] != missing_texture)
break;
}
if (i == 6) //couldn't find ANY sky textures.
forcedsky = NULL;
}
}
void R_DrawFastSky(batch_t *batch)
{
batch_t b = *batch;
b.shader = R_RegisterShader("fastsky", 0, "{\n"
"sort sky\n"
"{\n"
"map $whiteimage\n"
"rgbgen const $r_fastskycolour\n"
"}\n"
"surfaceparm nodlight\n"
"}\n");
b.skin = NULL;
b.texture = NULL;
BE_SubmitBatch(&b);
}
/* /*
================= =================
GL_DrawSkyChain GL_DrawSkyChain
@ -95,13 +125,32 @@ qboolean R_DrawSkyChain (batch_t *batch)
shader_t *skyshader; shader_t *skyshader;
texid_t *skyboxtex; texid_t *skyboxtex;
if (forcedskyshader) if (r_fastsky.value)
skyshader = forcedskyshader; {
else R_DrawFastSky(batch);
skyshader = batch->shader; return true; //depth will always be drawn with this pathway.
}
if (skyshader->prog || !skyboxface) if (forcedsky)
return false; {
skyshader = forcedsky;
if (forcedsky->numpasses)
{ //cubemap skies!
batch_t b = *batch;
b.shader = forcedsky;
b.skin = NULL;
b.texture = NULL;
BE_SubmitBatch(&b);
return true;
}
}
else
{
skyshader = batch->shader;
if (skyshader->prog) //glsl is expected to do the whole skybox/warpsky thing itself, with no assistance from this legacy code.
return false;
}
if (skyshader->skydome) if (skyshader->skydome)
skyboxtex = skyshader->skydome->farbox_textures; skyboxtex = skyshader->skydome->farbox_textures;
@ -126,6 +175,11 @@ qboolean R_DrawSkyChain (batch_t *batch)
else else
GL_DrawSkySphere(batch, skyshader); GL_DrawSkySphere(batch, skyshader);
} }
else if (batch->meshes)
{ //if you had wanted it invisible, you should have used nodraw.
R_DrawFastSky(batch);
return true; //depth will always be drawn with this pathway.
}
//neither skydomes nor skyboxes nor skygrids will have been drawn with the correct depth values for the sky. //neither skydomes nor skyboxes nor skygrids will have been drawn with the correct depth values for the sky.
//this can result in rooms behind the sky surfaces being visible. //this can result in rooms behind the sky surfaces being visible.
@ -387,7 +441,7 @@ static void R_CalcSkyChainBounds (batch_t *batch)
for (m = batch->firstmesh; m < batch->meshes; m++) for (m = batch->firstmesh; m < batch->meshes; m++)
{ {
mesh = batch->mesh[m]; mesh = batch->mesh[m];
if (!mesh->xyz_array) if (!mesh->xyz_array || !mesh->indexes)
continue; continue;
//triangulate //triangulate
for (i = 0; i < mesh->numindexes; i+=3) for (i = 0; i < mesh->numindexes; i+=3)
@ -714,6 +768,22 @@ static void GL_DrawSkyGrid (texnums_t *tex)
skyent.axis[2][2] = 1; skyent.axis[2][2] = 1;
skyent.scale = 1; skyent.scale = 1;
if (!skygridface)
skygridface = R_RegisterShader("skygridface", SUF_NONE,
"{\n"
"program default2d\n"
"{\n"
"map $diffuse\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"{\n"
"map $fullbright\n"
"blendfunc blend\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"}\n"
);
//FIXME: We should use the skybox clipping code and split the sphere into 6 sides. //FIXME: We should use the skybox clipping code and split the sphere into 6 sides.
b.meshes = 1; b.meshes = 1;
b.firstmesh = 0; b.firstmesh = 0;
@ -773,6 +843,17 @@ static void GL_DrawSkyBox (texid_t *texnums, batch_t *s)
skyfacemesh.numindexes = 6; skyfacemesh.numindexes = 6;
skyfacemesh.numvertexes = 4; skyfacemesh.numvertexes = 4;
if (!skyboxface)
skyboxface = R_RegisterShader("skyboxface", SUF_NONE,
"{\n"
"program default2d\n"
"{\n"
"map $diffuse\n"
"nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away.
"}\n"
"}\n"
);
for (i=0 ; i<6 ; i++) for (i=0 ; i<6 ; i++)
{ {
if (skymins[0][i] >= skymaxs[0][i] if (skymins[0][i] >= skymaxs[0][i]

View file

@ -143,7 +143,8 @@ struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, model_t *model, qb
ctx->shadows = shadows; ctx->shadows = shadows;
ctx->skiplit = skiplit; ctx->skiplit = skiplit;
} }
ctx->models[ctx->nummodels++] = model; if (ctx->nummodels < countof(ctx->models))
ctx->models[ctx->nummodels++] = model;
return ctx; return ctx;
} }
void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles) void LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles)

File diff suppressed because it is too large Load diff

View file

@ -504,7 +504,7 @@ typedef struct programshared_s
void *hull; void *hull;
void *domain; void *domain;
void *geom; void *geom;
void *layout; void *layouts[2];
#endif #endif
} hlsl; } hlsl;
#endif #endif
@ -606,6 +606,7 @@ struct shader_s
SHADER_HASDIFFUSE = 1 << 27, //has a T_GEN_DIFFUSE pass SHADER_HASDIFFUSE = 1 << 27, //has a T_GEN_DIFFUSE pass
SHADER_HASPALETTED = 1 << 28, //has a T_GEN_PALETTED pass SHADER_HASPALETTED = 1 << 28, //has a T_GEN_PALETTED pass
SHADER_HASCURRENTRENDER = 1 << 29, //has a $currentrender pass SHADER_HASCURRENTRENDER = 1 << 29, //has a $currentrender pass
SHADER_HASPORTAL = 1 << 30, //reflection image is actually a portal rather than a simple reflection (must be paired with SHADER_HASREFRACT)
} flags; } flags;
program_t *prog; program_t *prog;
@ -733,6 +734,7 @@ typedef struct
qboolean nv_tex_env_combine4; qboolean nv_tex_env_combine4;
qboolean env_add; qboolean env_add;
qboolean can_mipcap; // qboolean can_mipcap; //
qboolean havecubemaps; //since gl1.3, so pretty much everyone will have this... should probably only be set if we also have seamless or clamp-to-edge.
void (*pDeleteProg) (program_t *prog); void (*pDeleteProg) (program_t *prog);
qboolean (*pLoadBlob) (program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile); qboolean (*pLoadBlob) (program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile);

View file

@ -1022,25 +1022,25 @@ r_part teq2_heatbeam_steam
{ //FIXME { //FIXME
assoc placeholder assoc placeholder
} }
/*
r_part teq2_heatbeam_steam //r_part teq2_heatbeam_steam
{ //{
count 20 // count 20
colorindex 0xe0 7 // colorindex 0xe0 7
// magnitude 60 //// magnitude 60
texture "classicparticle" // texture "classicparticle"
tcoords 0 0 16 16 32 // tcoords 0 0 16 16 32
scale 1 // scale 1
alpha 1 // alpha 1
die 0.3 0.8 // die 0.3 0.8
randomvel 20 magnitude/3 // randomvel 20 magnitude/3
veladd magnitude // veladd magnitude
orgadd magnitude/10 // orgadd magnitude/10
spawnorg 4 // spawnorg 4
gravity -400 // gravity -400
scalefactor 0.8 // scalefactor 0.8
} //}
*/
//this is apparently just a trail effect (palette index specified by netcode) //this is apparently just a trail effect (palette index specified by netcode)
r_part teq2_forcewall r_part teq2_forcewall

View file

@ -3169,8 +3169,18 @@ retry:
switch(st[i].op) switch(st[i].op)
{ {
case OP_ADDRESS: case OP_ADDRESS:
if (st[i+1].op == OP_STOREP_V && st[i+1].b == st[i].c)
{ //following stores a vector to this field.
if (st[i].b+2 < pr_progs->numglobals)
{ //vectors are usually 3 fields. if they're not then we're screwed.
basictypetable[st[i].b+0] = ev_field;
basictypetable[st[i].b+1] = ev_field;
basictypetable[st[i].b+2] = ev_field;
}
break;
}
//fallthrough
case OP_LOAD_F: case OP_LOAD_F:
case OP_LOAD_V:
case OP_LOAD_S: case OP_LOAD_S:
case OP_LOAD_ENT: case OP_LOAD_ENT:
case OP_LOAD_FLD: case OP_LOAD_FLD:
@ -3180,6 +3190,25 @@ retry:
if (st[i].b < pr_progs->numglobals) if (st[i].b < pr_progs->numglobals)
basictypetable[st[i].b] = ev_field; basictypetable[st[i].b] = ev_field;
break; break;
case OP_LOAD_V:
if (st[i].b+2 < pr_progs->numglobals)
{ //vectors are usually 3 fields. if they're not then we're screwed.
basictypetable[st[i].b+0] = ev_field;
basictypetable[st[i].b+1] = ev_field;
basictypetable[st[i].b+2] = ev_field;
}
break;
}
}
for (i = 0; i < pr_progs->numglobaldefs; i++)
{
ddef16_t *gd = gd16+i;
switch(gd->type & ~(DEF_SAVEGLOBAL|DEF_SHARED))
{
case ev_field: //depend on _y _z to mark those globals.
basictypetable[gd->ofs] = ev_field;
break;
} }
} }

View file

@ -1113,7 +1113,10 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, char *filename, int linenum,
//we need to use the function table in order to set breakpoints in the right file. //we need to use the function table in order to set breakpoints in the right file.
for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++)
{ {
if (!stricmp(f->s_file+progfuncs->funcs.stringtable, filename)) const char *fncfile = f->s_file+progfuncs->funcs.stringtable;
if (fncfile[0] == '.' && fncfile[1] == '/')
fncfile+=2;
if (!stricmp(fncfile, filename))
{ {
for (i = f->first_statement; i < pr_progstate[pn].progs->numstatements; i++) for (i = f->first_statement; i < pr_progstate[pn].progs->numstatements; i++)
{ {
@ -1354,7 +1357,7 @@ static const char *lastfile = 0;
if (debugaction == DEBUG_TRACE_NORESUME) if (debugaction == DEBUG_TRACE_NORESUME)
continue; continue;
else if(debugaction == DEBUG_TRACE_ABORT) else if(debugaction == DEBUG_TRACE_ABORT)
progfuncs->funcs.parms->Abort ("Debugger Abort"); progfuncs->funcs.parms->Abort (fault?fault:"Debugger Abort");
else if (debugaction == DEBUG_TRACE_OFF) else if (debugaction == DEBUG_TRACE_OFF)
{ {
//if we're resuming, don't hit any lingering step-over triggers //if we're resuming, don't hit any lingering step-over triggers

View file

@ -511,6 +511,8 @@ model_t *QDECL SVPR_GetCModel(world_t *w, int modelindex)
mod = sv.models[modelindex]; mod = sv.models[modelindex];
if (mod && mod->loadstate != MLS_LOADED) if (mod && mod->loadstate != MLS_LOADED)
{ {
if (mod->loadstate == MLS_NOTLOADED)
Mod_LoadModel(mod, MLV_SILENT);
if (mod->loadstate == MLS_LOADING) if (mod->loadstate == MLS_LOADING)
COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);
if (mod->loadstate != MLS_LOADED) if (mod->loadstate != MLS_LOADED)
@ -1681,18 +1683,6 @@ void PR_SpawnInitialEntities(const char *file)
sv.world.edict_size = 0; sv.world.edict_size = 0;
} }
static qofs_t PR_ReadBytesString(char *str)
{
size_t u = strtoul(str, &str, 0);
if (*str == 'g')
u *= 1024*1024*1024;
if (*str == 'm')
u *= 1024*1024;
if (*str == 'k')
u *= 1024;
return u;
}
void SV_RegisterH2CustomTents(void); void SV_RegisterH2CustomTents(void);
void Q_InitProgs(void) void Q_InitProgs(void)
{ {
@ -3479,38 +3469,6 @@ static void QCBUILTIN PF_ss_LocalSound(pubprogfuncs_t *prinst, struct globalvars
#define PF_ss_LocalSound PF_Fixme #define PF_ss_LocalSound PF_Fixme
#endif #endif
unsigned int FTEToDPContents(unsigned int contents)
{
unsigned int r = 0;
if (contents & FTECONTENTS_SOLID)
r |= DPCONTENTS_SOLID;
if (contents & FTECONTENTS_WATER)
r |= DPCONTENTS_WATER;
if (contents & FTECONTENTS_SLIME)
r |= DPCONTENTS_SLIME;
if (contents & FTECONTENTS_LAVA)
r |= DPCONTENTS_LAVA;
if (contents & FTECONTENTS_SKY)
r |= DPCONTENTS_SKY;
if (contents & FTECONTENTS_BODY)
r |= DPCONTENTS_BODY;
if (contents & FTECONTENTS_CORPSE)
r |= DPCONTENTS_CORPSE;
if (contents & Q3CONTENTS_NODROP)
r |= DPCONTENTS_NODROP;
if (contents & FTECONTENTS_PLAYERCLIP)
r |= DPCONTENTS_PLAYERCLIP;
if (contents & FTECONTENTS_MONSTERCLIP)
r |= DPCONTENTS_MONSTERCLIP;
if (contents & Q3CONTENTS_DONOTENTER)
r |= DPCONTENTS_DONOTENTER;
if (contents & Q3CONTENTS_BOTCLIP)
r |= DPCONTENTS_BOTCLIP;
// if (contents & FTECONTENTS_OPAQUE)
// r |= DPCONTENTS_OPAQUE;
return r;
}
static void set_trace_globals(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace) static void set_trace_globals(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace)
{ {
pr_global_struct->trace_allsolid = trace->allsolid; pr_global_struct->trace_allsolid = trace->allsolid;
@ -5979,16 +5937,21 @@ static void QCBUILTIN PF_infokey_f (pubprogfuncs_t *prinst, struct globalvars_s
G_FLOAT(OFS_RETURN) = atof(value); G_FLOAT(OFS_RETURN) = atof(value);
} }
static void QCBUILTIN PF_sv_serverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_sv_serverkeystring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
char *value; const char *key = PR_GetStringOfs(prinst, OFS_PARM0);
const char *key; const char *value = Info_ValueForKey (svs.info, key);
G_INT(OFS_RETURN) = *value?PR_TempString(prinst, value):0;
key = PR_GetStringOfs(prinst, OFS_PARM1); }
value = Info_ValueForKey (svs.info, key); static void QCBUILTIN PF_sv_serverkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
G_INT(OFS_RETURN) = PR_TempString(prinst, value); {
const char *key = PR_GetStringOfs(prinst, OFS_PARM0);
const char *value = Info_ValueForKey (svs.info, key);
if (*value)
G_FLOAT(OFS_RETURN) = strtod(value, NULL);
else
G_FLOAT(OFS_RETURN) = (prinst->callargc>=2)?G_FLOAT(OFS_PARM1):0;
} }
/* /*
============== ==============
@ -7117,8 +7080,8 @@ const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned
case SCP_NETQUAKE: bp = "nq"; break; case SCP_NETQUAKE: bp = "nq"; break;
case SCP_BJP3: bp = "bjp3"; break; case SCP_BJP3: bp = "bjp3"; break;
case SCP_FITZ666: bp = "fitz666"; break; case SCP_FITZ666: bp = "fitz666"; break;
case SCP_DARKPLACES6: bp = "dp6"; break; case SCP_DARKPLACES6: bp = "dpp6"; break;
case SCP_DARKPLACES7: bp = "dp7"; break; case SCP_DARKPLACES7: bp = "dpp7"; break;
} }
Info_SetValueForKey(clfeatures, "basicprotocol", bp, sizeof(clfeatures)); Info_SetValueForKey(clfeatures, "basicprotocol", bp, sizeof(clfeatures));
Info_SetValueForKey(clfeatures, "guid", guid, sizeof(clfeatures)); Info_SetValueForKey(clfeatures, "guid", guid, sizeof(clfeatures));
@ -9168,14 +9131,14 @@ static void QCBUILTIN PF_setcolors (pubprogfuncs_t *prinst, struct globalvars_s
client->edict->v->team = (i & 15) + 1; client->edict->v->team = (i & 15) + 1;
sprintf(number, "%i", i>>4); sprintf(number, "%i", i>>4);
if (!strcmp(number, Info_ValueForKey(client->userinfo, "topcolor"))) if (strcmp(number, Info_ValueForKey(client->userinfo, "topcolor")))
{ {
Info_SetValueForKey(client->userinfo, "topcolor", number, sizeof(client->userinfo)); Info_SetValueForKey(client->userinfo, "topcolor", number, sizeof(client->userinfo));
key = "topcolor"; key = "topcolor";
} }
sprintf(number, "%i", i&15); sprintf(number, "%i", i&15);
if (!strcmp(number, Info_ValueForKey(client->userinfo, "bottomcolor"))) if (strcmp(number, Info_ValueForKey(client->userinfo, "bottomcolor")))
{ {
Info_SetValueForKey(client->userinfo, "bottomcolor", number, sizeof(client->userinfo)); Info_SetValueForKey(client->userinfo, "bottomcolor", number, sizeof(client->userinfo));
key = key?"*bothcolours":"bottomcolor"; key = key?"*bothcolours":"bottomcolor";
@ -9464,9 +9427,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars
pmove_mins[i] = pmove.origin[i] - 256; pmove_mins[i] = pmove.origin[i] - 256;
pmove_maxs[i] = pmove.origin[i] + 256; pmove_maxs[i] = pmove.origin[i] + 256;
} }
AddLinksToPmove(ent, sv.world.areanodes); AddAllLinksToPmove(&sv.world, (wedict_t*)ent);
// AddAllEntsToPmove();
AddLinksToPmove_Force ( ent, &sv.world.portallist );
SV_PreRunCmd(); SV_PreRunCmd();
@ -9841,6 +9802,11 @@ static void QCBUILTIN PF_setpause(pubprogfuncs_t *prinst, struct globalvars_s *p
G_FLOAT(OFS_RETURN) = !!(sv.paused&PAUSE_EXPLICIT); G_FLOAT(OFS_RETURN) = !!(sv.paused&PAUSE_EXPLICIT);
if (sv.paused != pause) if (sv.paused != pause)
{ {
if (pause&PAUSE_EXPLICIT)
SV_BroadcastTPrintf (PRINT_HIGH, "game paused\n");
else
SV_BroadcastTPrintf (PRINT_HIGH, "game unpaused\n");
sv.paused = pause; sv.paused = pause;
sv.pausedstart = Sys_DoubleTime(); sv.pausedstart = Sys_DoubleTime();
} }
@ -9976,7 +9942,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"remove", PF_Remove, 15, 15, 15, 0, D("void(entity e)", "Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.")}, {"remove", PF_Remove, 15, 15, 15, 0, D("void(entity e)", "Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.")},
{"traceline", PF_svtraceline, 16, 16, 16, 0, D("void(vector v1, vector v2, float flags, entity ent)", "Traces a thin line through the world from v1 towards v2.\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\nThere are no side effects beyond the trace_* globals being written.\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\nflags&MOVE_MISSILE will impact with increased size.\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\nflags&MOVE_TRIGGERS will also stop on triggers\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.")}, {"traceline", PF_svtraceline, 16, 16, 16, 0, D("void(vector v1, vector v2, float flags, entity ent)", "Traces a thin line through the world from v1 towards v2.\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\nThere are no side effects beyond the trace_* globals being written.\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\nflags&MOVE_MISSILE will impact with increased size.\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\nflags&MOVE_TRIGGERS will also stop on triggers\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.")},
{"checkclient", PF_checkclient, 17, 17, 17, 0, D("entity()", "Returns one of the player entities. The returned player will change periodically.")}, {"checkclient", PF_checkclient, 17, 17, 17, 0, D("entity()", "Returns one of the player entities. The returned player will change periodically.")},
{"find", PF_FindString, 18, 18, 18, 0, D("entity(entity start, .string fld, string match)", "Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.")}, {"find", PF_FindString, 18, 18, 18, 0, D("entity(entity start, .string fld, string match)", "Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\nIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep).")},
{"precache_sound", PF_precache_sound, 19, 19, 19, 0, D("string(string s)", "Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard.")}, {"precache_sound", PF_precache_sound, 19, 19, 19, 0, D("string(string s)", "Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard.")},
{"precache_model", PF_precache_model, 20, 20, 20, 0, D("string(string s)", "Precaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name.\nModelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server.")}, {"precache_model", PF_precache_model, 20, 20, 20, 0, D("string(string s)", "Precaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name.\nModelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server.")},
{"stuffcmd", PF_stuffcmd, 21, 21, 21, 0, D("void(entity client, string s)", "Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\nThis builtin is generally considered evil.")}, {"stuffcmd", PF_stuffcmd, 21, 21, 21, 0, D("void(entity client, string s)", "Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\nThis builtin is generally considered evil.")},
@ -10305,7 +10271,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"checkpvs", PF_checkpvs, 0, 0, 0, 240, "float(vector viewpos, entity entity)"}, {"checkpvs", PF_checkpvs, 0, 0, 0, 240, "float(vector viewpos, entity entity)"},
{"matchclientname", PF_matchclient, 0, 0, 0, 241, "entity(string match, optional float matchnum)"}, {"matchclientname", PF_matchclient, 0, 0, 0, 241, "entity(string match, optional float matchnum)"},
{"sendpacket", PF_SendPacket, 0, 0, 0, 242, "void(string dest, string content)"},// (FTE_QC_SENDPACKET) {"sendpacket", PF_SendPacket, 0, 0, 0, 242, "void(string destaddress, string content)"},// (FTE_QC_SENDPACKET)
// {"bulleten", PF_bulleten, 0, 0, 0, 243}, (removed builtin) // {"bulleten", PF_bulleten, 0, 0, 0, 243}, (removed builtin)
@ -10360,15 +10326,15 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
#ifdef TERRAIN #ifdef TERRAIN
{"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, D("void(float action, optional vector pos, optional float radius, optional float quant, ...)", "Realtime terrain editing. Actions are the TEREDIT_ constants.")},// (??FTE_TERRAIN_EDIT?? {"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, D("void(float action, optional vector pos, optional float radius, optional float quant, ...)", "Realtime terrain editing. Actions are the TEREDIT_ constants.")},// (??FTE_TERRAIN_EDIT??
#define qcbrushface \ #define qcbrushface \
"typedef struct\n{\n" \ "typedef struct\n{\n" \
"\tstring\tshadername;\n" \ "\tstring\tshadername;\n" \
"\tvector\tplanenormal;\n" \ "\tvector\tplanenormal;\n" \
"\tfloat\tplanedist;\n" \ "\tfloat\tplanedist;\n" \
"\tvector\tsdir;\n" \ "\tvector\tsdir;\n" \
"\tfloat\tsbias;\n" \ "\tfloat\tsbias;\n" \
"\tvector\ttdir;\n" \ "\tvector\ttdir;\n" \
"\tfloat\ttbias;\n" \ "\tfloat\ttbias;\n" \
"} brushface_t;\n" "} brushface_t;\n"
{"brush_get", PF_brush_get, 0, 0, 0, 0, D(qcbrushface "int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)", "Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.")}, {"brush_get", PF_brush_get, 0, 0, 0, 0, D(qcbrushface "int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)", "Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.")},
{"brush_create", PF_brush_create, 0, 0, 0, 0, D("int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid)", "Inserts a new brush into the model. Return value is the new brush's id.")}, {"brush_create", PF_brush_create, 0, 0, 0, 0, D("int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid)", "Inserts a new brush into the model. Return value is the new brush's id.")},
@ -10484,6 +10450,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"runstandardplayerphysics",PF_runclientphys,0,0,0, 347, D("void(entity ent)", "Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement.")}, {"runstandardplayerphysics",PF_runclientphys,0,0,0, 347, D("void(entity ent)", "Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement.")},
{"getplayerkeyvalue", PF_Fixme,0, 0, 0, 348, D("string(float playernum, string keyname)", "Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\nAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness.")},// (EXT_CSQC) {"getplayerkeyvalue", PF_Fixme,0, 0, 0, 348, D("string(float playernum, string keyname)", "Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\nAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness.")},// (EXT_CSQC)
{"getplayerkeyfloat", PF_Fixme,0, 0, 0, 0, D("float(float playernum, string keyname, optional float assumevalue)", "Cheaper version of getplayerkeyvalue that avoids the need for so many tempstrings.")},
{"isdemo", PF_Fixme, 0, 0, 0, 349, D("float()", "Returns if the client is currently playing a demo or not")},// (EXT_CSQC) {"isdemo", PF_Fixme, 0, 0, 0, 349, D("float()", "Returns if the client is currently playing a demo or not")},// (EXT_CSQC)
{"isserver", PF_Fixme, 0, 0, 0, 350, D("float()", "Returns if the client is acting as the server (aka: listen server)")},//(EXT_CSQC) {"isserver", PF_Fixme, 0, 0, 0, 350, D("float()", "Returns if the client is acting as the server (aka: listen server)")},//(EXT_CSQC)
@ -10491,10 +10458,12 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"setup_reverb", PF_Fixme, 0, 0, 0, 0, D("typedef struct {\n\tfloat flDensity;\n\tfloat flDiffusion;\n\tfloat flGain;\n\tfloat flGainHF;\n\tfloat flGainLF;\n\tfloat flDecayTime;\n\tfloat flDecayHFRatio;\n\tfloat flDecayLFRatio;\n\tfloat flReflectionsGain;\n\tfloat flReflectionsDelay;\n\tvector flReflectionsPan;\n\tfloat flLateReverbGain;\n\tfloat flLateReverbDelay;\n\tvector flLateReverbPan;\n\tfloat flEchoTime;\n\tfloat flEchoDepth;\n\tfloat flModulationTime;\n\tfloat flModulationDepth;\n\tfloat flAirAbsorptionGainHF;\n\tfloat flHFReference;\n\tfloat flLFReference;\n\tfloat flRoomRolloffFactor;\n\tint iDecayHFLimit;\n} reverbinfo_t;\nvoid(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t)", "Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL.")}, {"setup_reverb", PF_Fixme, 0, 0, 0, 0, D("typedef struct {\n\tfloat flDensity;\n\tfloat flDiffusion;\n\tfloat flGain;\n\tfloat flGainHF;\n\tfloat flGainLF;\n\tfloat flDecayTime;\n\tfloat flDecayHFRatio;\n\tfloat flDecayLFRatio;\n\tfloat flReflectionsGain;\n\tfloat flReflectionsDelay;\n\tvector flReflectionsPan;\n\tfloat flLateReverbGain;\n\tfloat flLateReverbDelay;\n\tvector flLateReverbPan;\n\tfloat flEchoTime;\n\tfloat flEchoDepth;\n\tfloat flModulationTime;\n\tfloat flModulationDepth;\n\tfloat flAirAbsorptionGainHF;\n\tfloat flHFReference;\n\tfloat flLFReference;\n\tfloat flRoomRolloffFactor;\n\tint iDecayHFLimit;\n} reverbinfo_t;\nvoid(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t)", "Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL.")},
{"registercommand", PF_Fixme, 0, 0, 0, 352, D("void(string cmdname)", "Register the given console command, for easy console use.\nConsole commands that are later used will invoke CSQC_ConsoleCommand.")},//(EXT_CSQC) {"registercommand", PF_Fixme, 0, 0, 0, 352, D("void(string cmdname)", "Register the given console command, for easy console use.\nConsole commands that are later used will invoke CSQC_ConsoleCommand.")},//(EXT_CSQC)
{"wasfreed", PF_WasFreed,0, 0, 0, 353, D("float(entity ent)", "Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust.")},//(EXT_CSQC) (should be availabe on server too) {"wasfreed", PF_WasFreed,0, 0, 0, 353, D("float(entity ent)", "Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust.")},//(EXT_CSQC) (should be availabe on server too)
{"serverkey", PF_sv_serverkey,0, 0, 0, 354, D("string(string key)", "Look up a key in the server's public serverinfo string")},// {"serverkey", PF_sv_serverkeystring,0,0, 0, 354, D("string(string key)", "Look up a key in the server's public serverinfo string")},//
{"serverkeyfloat", PF_sv_serverkeyfloat,0,0, 0, 0, D("float(string key, optional float assumevalue)", "Version of serverkey that returns the value as a float (which avoids tempstrings).")},//
{"getentitytoken", PF_Fixme, 0, 0, 0, 355, D("string(optional string resetstring)", "Grab the next token in the map's entity lump.\nIf resetstring is not specified, the next token will be returned with no other sideeffects.\nIf empty, will reset from the map before returning the first token, probably {.\nIf not empty, will tokenize from that string instead.\nAlways returns tempstrings.")},//; {"getentitytoken", PF_Fixme, 0, 0, 0, 355, D("string(optional string resetstring)", "Grab the next token in the map's entity lump.\nIf resetstring is not specified, the next token will be returned with no other sideeffects.\nIf empty, will reset from the map before returning the first token, probably {.\nIf not empty, will tokenize from that string instead.\nAlways returns tempstrings.")},//;
{"findfont", PF_Fixme, 0, 0, 0, 356, D("float(string s)", "Looks up a named font slot. Matches the actual font name as a last resort.")},//; {"findfont", PF_Fixme, 0, 0, 0, 356, D("float(string s)", "Looks up a named font slot. Matches the actual font name as a last resort.")},//;
{"loadfont", PF_Fixme, 0, 0, 0, 357, D("float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset)", "too convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\"\", \"cour\", \"16\", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high), if you have the freetype2 library in windows..")}, {"loadfont", PF_Fixme, 0, 0, 0, 357, D("float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset)", "too convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\"\", \"cour\", \"16\", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high), if you have the freetype2 library in windows..")},
//358
{"sendevent", PF_Fixme, 0, 0, 0, 359, D("void(string evname, string evargs, ...)", "Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors.")},// (EXT_CSQC_1) {"sendevent", PF_Fixme, 0, 0, 0, 359, D("void(string evname, string evargs, ...)", "Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors.")},// (EXT_CSQC_1)
{"readbyte", PF_Fixme, 0, 0, 0, 360, "float()"},// (EXT_CSQC) {"readbyte", PF_Fixme, 0, 0, 0, 360, "float()"},// (EXT_CSQC)
@ -11561,7 +11530,8 @@ void PR_DumpPlatform_f(void)
{"CSQC_RendererRestarted", "void(string rendererdescription)", CS, "Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated."}, {"CSQC_RendererRestarted", "void(string rendererdescription)", CS, "Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated."},
{"CSQC_ConsoleCommand", "float(string cmd)", CS, "Called if the user uses any console command registed via registercommand."}, {"CSQC_ConsoleCommand", "float(string cmd)", CS, "Called if the user uses any console command registed via registercommand."},
{"CSQC_ConsoleLink", "float(string text, string info)", CS, "Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself.\nWARNING: link text can potentially come from other players, so be careful about what you allow to be changed."}, {"CSQC_ConsoleLink", "float(string text, string info)", CS, "Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself.\nWARNING: link text can potentially come from other players, so be careful about what you allow to be changed."},
{"CSQC_Ent_Update", "void(float isnew)", CS}, {"CSQC_Ent_Spawn", "void(float entnum)", CS, "Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless)."},
{"CSQC_Ent_Update", "void(float isnew)", CS, "Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately."},
{"CSQC_Ent_Remove", "void()", CS}, {"CSQC_Ent_Remove", "void()", CS},
{"CSQC_Event_Sound", "float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags"/*", float timeofs*/")", CS}, {"CSQC_Event_Sound", "float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags"/*", float timeofs*/")", CS},
// {"CSQC_ServerSound", "//void()", CS}, // {"CSQC_ServerSound", "//void()", CS},

View file

@ -424,6 +424,11 @@ comextqcfields
} comentvars_t; } comentvars_t;
#endif #endif
#ifdef USEAREAGRID
#define AREAGRIDPERENT 16
#endif
#ifdef USERBE #ifdef USERBE
typedef struct typedef struct
{ {

View file

@ -75,7 +75,12 @@ typedef struct edict_s
/*qc lib doesn't care about the rest*/ /*qc lib doesn't care about the rest*/
/*these are shared with csqc*/ /*these are shared with csqc*/
#ifdef USEAREAGRID
areagridlink_t gridareas[AREAGRIDPERENT]; //on overflow, use the inefficient overflow list.
size_t gridareasequence; //used to avoid iterrating the same ent twice.
#else
link_t area; link_t area;
#endif
pvscache_t pvsinfo; pvscache_t pvsinfo;
int lastruntime; int lastruntime;
int solidsize; int solidsize;

View file

@ -864,7 +864,7 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
SV_SpawnParmsToQC(host_client); SV_SpawnParmsToQC(host_client);
pr_global_struct->time = sv.world.physicstime; pr_global_struct->time = sv.world.physicstime;
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent); pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);
ent->area.next = ent->area.prev = NULL; World_UnlinkEdict((wedict_t*)ent);
G_FLOAT(OFS_PARM0) = sv.time-host_client->spawninfotime; G_FLOAT(OFS_PARM0) = sv.time-host_client->spawninfotime;
PR_ExecuteProgram(svprogfuncs, eval->function); PR_ExecuteProgram(svprogfuncs, eval->function);

View file

@ -489,6 +489,10 @@ typedef struct client_s
// extracted from userinfo // extracted from userinfo
char guid[64]; /*+2 for split+pad*/ char guid[64]; /*+2 for split+pad*/
int messagelevel; // for filtering printed messages int messagelevel; // for filtering printed messages
#ifndef NOLEGACY
float *dp_ping;
float *dp_pl;
#endif
// the datagram is written to after every frame, but only cleared // the datagram is written to after every frame, but only cleared
// when it is sent out to the client. overflow is tolerated. // when it is sent out to the client. overflow is tolerated.
@ -586,7 +590,7 @@ typedef struct client_s
int chokecount; int chokecount;
qboolean waschoked; qboolean waschoked;
int delta_sequence; // -1 = no compression int delta_sequence; // -1 = no compression
int last_sequence; int last_sequence; //last inputframe sequence received
netchan_t netchan; netchan_t netchan;
qboolean isindependant; qboolean isindependant;
@ -655,7 +659,7 @@ typedef struct client_s
unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat
//speed cheat testing //speed cheat testing
#define NEWSPEEDCHEATPROT #define NEWSPEEDCHEATPROT
int msecs; float msecs;
#ifndef NEWSPEEDCHEATPROT #ifndef NEWSPEEDCHEATPROT
int msec_cheating; int msec_cheating;
float last_check; float last_check;
@ -1168,6 +1172,7 @@ typedef struct pubsubserver_s
int transferingplayers; int transferingplayers;
netadr_t addrv4; netadr_t addrv4;
netadr_t addrv6; netadr_t addrv6;
char printtext[4096]; //to split it into lines.
} pubsubserver_t; } pubsubserver_t;
extern qboolean isClusterSlave; extern qboolean isClusterSlave;
void SSV_UpdateAddresses(void); void SSV_UpdateAddresses(void);
@ -1587,8 +1592,13 @@ void SV_CheckTimer(void);
void SV_LogPlayer(client_t *cl, char *msg); void SV_LogPlayer(client_t *cl, char *msg);
extern vec3_t pmove_mins, pmove_maxs; //abs min/max extents extern vec3_t pmove_mins, pmove_maxs; //abs min/max extents
void AddLinksToPmove ( edict_t *player, areanode_t *node ); #ifdef USEAREAGRID
void AddLinksToPmove_Force ( edict_t *player, areanode_t *node ); void AddAllLinksToPmove (world_t *w, wedict_t *player);
#else
void AddLinksToPmove (world_t *w, wedict_t *player, areanode_t *node);
void AddLinksToPmove_Force (world_t *w, wedict_t *player, areanode_t *node);
#define AddAllLinksToPmove(w,p) do{AddLinksToPmove(w,p,(w)->areanodes);AddLinksToPmove_Force(w,p,&(w)->portallist);}while(0)
#endif
#ifdef HLSERVER #ifdef HLSERVER

View file

@ -1984,7 +1984,7 @@ static void SV_Status_f (void)
else else
{ {
#define COLUMNS C_FRAGS C_USERID C_ADDRESS C_NAME C_RATE C_PING C_DROP C_DLP C_DLS C_PROT C_ADDRESS2 #define COLUMNS C_FRAGS C_USERID C_ADDRESS C_NAME C_RATE C_PING C_DROP C_DLP C_DLS C_PROT C_ADDRESS2
#define C_FRAGS COLUMN(0, "frags", Con_Printf("%5i ", (int)cl->old_frags)) #define C_FRAGS COLUMN(0, "frags", if (cl->spectator==1)Con_Printf("%-5s ", "spec"); else Con_Printf("%5i ", (int)cl->old_frags))
#define C_USERID COLUMN(1, "userid", Con_Printf("%6i ", (int)cl->userid)) #define C_USERID COLUMN(1, "userid", Con_Printf("%6i ", (int)cl->userid))
#define C_ADDRESS COLUMN(2, "address ", Con_Printf("%-16.16s", s)) #define C_ADDRESS COLUMN(2, "address ", Con_Printf("%-16.16s", s))
#define C_NAME COLUMN(3, "name ", Con_Printf("%-16.16s", cl->name)) #define C_NAME COLUMN(3, "name ", Con_Printf("%-16.16s", cl->name))
@ -2052,24 +2052,17 @@ static void SV_Status_f (void)
case SCP_BAD: case SCP_BAD:
p = ""; p = "";
break; break;
case SCP_QUAKEWORLD: case SCP_QUAKEWORLD: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"fteq":"qw"; break;
if (cl->spectator)
p = "s";
else if (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
p = "fte";
else
p = "qw";
break;
case SCP_QUAKE2: p = "q2"; break; case SCP_QUAKE2: p = "q2"; break;
case SCP_QUAKE3: p = "q3"; break; case SCP_QUAKE3: p = "q3"; break;
case SCP_NETQUAKE: p = "nq"; break; case SCP_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"nq"; break;
case SCP_BJP3: p = "bjp3"; break; case SCP_BJP3: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"bjp3"; break;
case SCP_FITZ666: p = "fitz"; break; case SCP_FITZ666: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"fitz"; break;
case SCP_DARKPLACES6: p = "dp6"; break; case SCP_DARKPLACES6: p = "dpp6"; break;
case SCP_DARKPLACES7: p = "dp7"; break; case SCP_DARKPLACES7: p = "dpp7"; break;
} }
if (cl->state == cs_connected && cl->protocol>=SCP_NETQUAKE) if (cl->state == cs_connected && cl->protocol>=SCP_NETQUAKE)
p = "nq"; p = "nq"; //not actually known yet.
else if (cl->state == cs_zombie || cl->state == cs_loadzombie) else if (cl->state == cs_zombie || cl->state == cs_loadzombie)
p = "zom"; p = "zom";
@ -3008,6 +3001,11 @@ void SV_InitOperatorCommands (void)
Cmd_AddCommand ("sv", SV_SendGameCommand_f); Cmd_AddCommand ("sv", SV_SendGameCommand_f);
Cmd_AddCommand ("mod", SV_SendGameCommand_f); Cmd_AddCommand ("mod", SV_SendGameCommand_f);
#ifdef SUBSERVERS
Cmd_AddCommand ("ssv", MSV_SubServerCommand_f);
Cmd_AddCommand ("ssv_all", MSV_SubServerCommand_f);
Cmd_AddCommandAD ("mapcluster", MSV_MapCluster_f, SV_Map_c, "Sets this server up as a cluster-server gateway. Additional processes will be used to host individual maps. If an argument is given then that will be the name of the map that new clients will initially be directed to. This can also be used for single-player to off-load nearly all server functions - use the 'ssv' command to direct each subserver.");
#endif
Cmd_AddCommand ("killserver", SV_KillServer_f); Cmd_AddCommand ("killserver", SV_KillServer_f);
Cmd_AddCommandD ("precaches", SV_PrecacheList_f, "Displays a list of current server precaches."); Cmd_AddCommandD ("precaches", SV_PrecacheList_f, "Displays a list of current server precaches.");
Cmd_AddCommandAD ("map", SV_Map_f, SV_Map_c, "Changes map. If a second argument is specified then that is normally the name of the initial start spot."); Cmd_AddCommandAD ("map", SV_Map_f, SV_Map_c, "Changes map. If a second argument is specified then that is normally the name of the initial start spot.");

View file

@ -10,6 +10,10 @@
//":dm4" finds any server running dm4. starts a new one if none are running dm4. //":dm4" finds any server running dm4. starts a new one if none are running dm4.
//"dm4" is ambiguous, in the case of a map beginning with a number (bah). don't use. //"dm4" is ambiguous, in the case of a map beginning with a number (bah). don't use.
//FIXME: nq protocols not supported.
//FIXME: deadlocks when both gw and ss both fill their pipe buffers.
//FIXME: no networking for remote nodes.
#ifdef SUBSERVERS #ifdef SUBSERVERS
#ifdef SQL #ifdef SQL
@ -384,6 +388,23 @@ void MSV_SubServerCommand_f(void)
MSV_InstructSlave(id, &buf); MSV_InstructSlave(id, &buf);
} }
static void MSV_PrintFromSubServer(pubsubserver_t *s, const char *newtext)
{
char *nl;
Q_strncatz(s->printtext, newtext, sizeof(s->printtext));
while((nl = strchr(s->printtext, '\n')))
{ //FIXME: handle overflows.
*nl++ = 0;
Con_Printf("^6%i(%s)^7: %s\n", s->id, s->name, s->printtext);
memmove(s->printtext, nl, strlen(nl)+1);
}
if (strlen(s->printtext) > sizeof(s->printtext)/2)
{
Con_Printf("^6%i(%s)^7: %s\n", s->id, s->name, s->printtext);
*s->printtext = 0;
}
}
void MSV_ReadFromSubServer(pubsubserver_t *s) void MSV_ReadFromSubServer(pubsubserver_t *s)
{ {
sizebuf_t send; sizebuf_t send;
@ -402,7 +423,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
Sys_Error("Corrupt message (%i) from SubServer %i:%s", c, s->id, s->name); Sys_Error("Corrupt message (%i) from SubServer %i:%s", c, s->id, s->name);
break; break;
case ccmd_print: case ccmd_print:
Con_Printf("^6%i(%s)^7: %s", s->id, s->name, MSG_ReadString()); MSV_PrintFromSubServer(s, MSG_ReadString());
break; break;
case ccmd_saveplayer: case ccmd_saveplayer:
{ {
@ -779,7 +800,6 @@ void SSV_ReadFromControlServer(void)
Q_strncpyz(cl->guid, clguid, sizeof(cl->guid)); Q_strncpyz(cl->guid, clguid, sizeof(cl->guid));
Q_strncpyz(cl->namebuf, plname, sizeof(cl->namebuf)); Q_strncpyz(cl->namebuf, plname, sizeof(cl->namebuf));
cl->name = cl->namebuf; cl->name = cl->namebuf;
sv.spawned_client_slots++;
memset(&cl->netchan, 0, sizeof(cl->netchan)); memset(&cl->netchan, 0, sizeof(cl->netchan));
SV_GetNewSpawnParms(cl); SV_GetNewSpawnParms(cl);
} }
@ -787,7 +807,7 @@ void SSV_ReadFromControlServer(void)
} }
else else
{ {
Con_Printf("%s: server full!\n", sv.modelname); Con_Printf("%s: server full!\n", svs.name);
} }
j = MSG_ReadByte(); j = MSG_ReadByte();
@ -969,7 +989,7 @@ void SSV_UpdateAddresses(void)
send.cursize = 2; send.cursize = 2;
MSG_WriteByte(&send, ccmd_serveraddress); MSG_WriteByte(&send, ccmd_serveraddress);
MSG_WriteString(&send, sv.modelname); MSG_WriteString(&send, svs.name);
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i])); MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i]));
MSG_WriteByte(&send, 0); MSG_WriteByte(&send, 0);

View file

@ -996,7 +996,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
if (FS_FLocateFile(sv.modelname,FSLF_IFFOUND, &loc) && FS_GetLocMTime(&loc, &filetime)) if (FS_FLocateFile(sv.modelname,FSLF_IFFOUND, &loc) && FS_GetLocMTime(&loc, &filetime))
{ {
if (filetime > sv.world.worldmodel->mtime) if (filetime > sv.world.worldmodel->mtime && sv.world.worldmodel->mtime)
{ {
COM_WorkerFullSync(); //sync all the workers, just in case. COM_WorkerFullSync(); //sync all the workers, just in case.
Mod_PurgeModel(sv.world.worldmodel, MP_RESET); //nuke it now Mod_PurgeModel(sv.world.worldmodel, MP_RESET); //nuke it now
@ -1668,7 +1668,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
SV_SpawnParmsToQC(host_client); SV_SpawnParmsToQC(host_client);
SV_SetUpClientEdict(host_client, sv_player); SV_SetUpClientEdict(host_client, sv_player);
#ifndef NOLEGACY #ifndef NOLEGACY
sv_player->xv->clientcolors = atoi(Info_ValueForKey(host_client->userinfo, "topcolor"))*16 + atoi(Info_ValueForKey(host_client->userinfo, "bottomcolor")); sv_player->xv->clientcolors = host_client->playercolor;
#endif #endif
// call the spawn function // call the spawn function

View file

@ -2663,9 +2663,9 @@ client_t *SVC_DirectConnect(void)
protocol = SCP_FITZ666; protocol = SCP_FITZ666;
else if (!strcmp(sv_protocol_nq.string, "bjp") || !strcmp(sv_protocol_nq.string, "bjp3")) else if (!strcmp(sv_protocol_nq.string, "bjp") || !strcmp(sv_protocol_nq.string, "bjp3"))
protocol = SCP_BJP3; protocol = SCP_BJP3;
else if (!strcmp(sv_protocol_nq.string, "dp6")) else if (!strcmp(sv_protocol_nq.string, "dpp6") || !strcmp(sv_protocol_nq.string, "dp6"))
protocol = SCP_DARKPLACES6; protocol = SCP_DARKPLACES6;
else if (!strcmp(sv_protocol_nq.string, "dp7")) else if (!strcmp(sv_protocol_nq.string, "dpp7") || !strcmp(sv_protocol_nq.string, "dp7"))
protocol = SCP_DARKPLACES7; protocol = SCP_DARKPLACES7;
else if (!strcmp(sv_protocol_nq.string, "id") || !strcmp(sv_protocol_nq.string, "vanilla")) else if (!strcmp(sv_protocol_nq.string, "id") || !strcmp(sv_protocol_nq.string, "vanilla"))
protocol = SCP_NETQUAKE; protocol = SCP_NETQUAKE;
@ -5147,12 +5147,6 @@ void SV_InitLocal (void)
Cmd_AddCommand ("sv_impulse", SV_Impulse_f); Cmd_AddCommand ("sv_impulse", SV_Impulse_f);
#ifdef SUBSERVERS
Cmd_AddCommand ("ssv", MSV_SubServerCommand_f);
Cmd_AddCommand ("ssv_all", MSV_SubServerCommand_f);
Cmd_AddCommand ("mapcluster", MSV_MapCluster_f);
#endif
Cmd_AddCommand ("openroute", SV_OpenRoute_f); Cmd_AddCommand ("openroute", SV_OpenRoute_f);
#ifndef NOBUILTINMENUS #ifndef NOBUILTINMENUS
@ -5347,39 +5341,42 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
newname[0] = 0; newname[0] = 0;
deleetstring(basic, newname); deleetstring(basic, newname);
if ((!basic[0] && cl->protocol != SCP_BAD) || strstr(basic, "console")) if (cl->protocol != SCP_BAD)
strcpy(newname, "unnamed"); { //don't bother validating bot names. The gamecode is expected to not be stupid.
if (!basic[0] || strstr(basic, "console"))
strcpy(newname, "unnamed");
// check to see if another user by the same name exists // check to see if another user by the same name exists
while (1) while (1)
{
for (i=0, client = svs.clients ; i<svs.allocated_client_slots ; i++, client++)
{ {
if (client->state < cs_connected || client == cl) for (i=0, client = svs.clients ; i<svs.allocated_client_slots ; i++, client++)
continue; {
if (!stricmp(client->name, newname)) if (client->state < cs_connected || client == cl)
continue;
if (!stricmp(client->name, newname))
break;
}
if (i != svs.allocated_client_slots)
{ // dup name
if (strlen(newname) > sizeof(cl->namebuf) - 1)
newname[sizeof(cl->namebuf) - 4] = 0;
p = newname;
if (newname[0] == '(')
{
if (newname[2] == ')')
p = newname + 3;
else if (val[3] == ')')
p = newname + 4;
}
memmove(newname+10, p, strlen(p)+1);
sprintf(newname, "(%d)%-.40s", dupc++, newname+10);
}
else
break; break;
} }
if (i != svs.allocated_client_slots)
{ // dup name
if (strlen(newname) > sizeof(cl->namebuf) - 1)
newname[sizeof(cl->namebuf) - 4] = 0;
p = newname;
if (newname[0] == '(')
{
if (newname[2] == ')')
p = newname + 3;
else if (val[3] == ')')
p = newname + 4;
}
memmove(newname+10, p, strlen(p)+1);
sprintf(newname, "(%d)%-.40s", dupc++, newname+10);
}
else
break;
} }
if (!cl->drop && strncmp(newname, cl->name, sizeof(cl->namebuf)-1)) if (!cl->drop && strncmp(newname, cl->name, sizeof(cl->namebuf)-1))

View file

@ -813,11 +813,19 @@ static qboolean WPhys_PushAngles (world_t *w, wedict_t *pusher, vec3_t move, vec
continue; continue;
} }
//these pushes are contents brushes, and are not solid. water cannot crush. the player just enters the water. //some pushers are contents brushes, and are not solid. water cannot crush. the player just enters the water.
//but, the player will be moved along with the water if possible. //but, the player will be moved along with the water if possible.
if (pusher->v->skin < 0) if (pusher->v->skin < 0)
continue; continue;
if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
{ // corpse
check->v->mins[0] = check->v->mins[1] = 0;
VectorCopy (check->v->mins, check->v->maxs);
World_LinkEdict (w, check, false);
continue;
}
// Con_Printf("Pusher hit %s\n", PR_GetString(w->progs, check->v->classname)); // Con_Printf("Pusher hit %s\n", PR_GetString(w->progs, check->v->classname));
if (pusher->v->blocked) if (pusher->v->blocked)
{ {
@ -846,7 +854,7 @@ static qboolean WPhys_PushAngles (world_t *w, wedict_t *pusher, vec3_t move, vec
//FIXME: is there a better way to handle this? //FIXME: is there a better way to handle this?
// see if anything we moved has touched a trigger // see if anything we moved has touched a trigger
for (p=pushed_p-1 ; p>=pushed ; p--) for (p=pushed_p-1 ; p>=pushed ; p--)
World_TouchLinks (w, p->ent, w->areanodes); World_TouchAllLinks (w, p->ent);
return true; return true;
} }
@ -1329,6 +1337,7 @@ static void WPhys_Physics_Toss (world_t *w, wedict_t *ent)
// add gravity // add gravity
if (ent->v->movetype != MOVETYPE_FLY if (ent->v->movetype != MOVETYPE_FLY
&& ent->v->movetype != MOVETYPE_FLY_WORLDONLY
&& ent->v->movetype != MOVETYPE_FLYMISSILE && ent->v->movetype != MOVETYPE_FLYMISSILE
&& ent->v->movetype != MOVETYPE_BOUNCEMISSILE && ent->v->movetype != MOVETYPE_BOUNCEMISSILE
&& ent->v->movetype != MOVETYPE_H2SWIM) && ent->v->movetype != MOVETYPE_H2SWIM)
@ -2190,7 +2199,6 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent)
if (!(ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots) && w == &sv.world) if (!(ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots) && w == &sv.world)
World_LinkEdict (w, ent, true); World_LinkEdict (w, ent, true);
#endif #endif
break; break;
#ifdef USERBE #ifdef USERBE
case MOVETYPE_PHYSICS: case MOVETYPE_PHYSICS:

View file

@ -2020,8 +2020,6 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
if (bits & FITZSU_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v->weaponframe >> 8); if (bits & FITZSU_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v->weaponframe >> 8);
if (bits & FITZSU_WEAPONALPHA) MSG_WriteByte (msg, ent->xv->alpha*255); if (bits & FITZSU_WEAPONALPHA) MSG_WriteByte (msg, ent->xv->alpha*255);
} }
// }
#endif #endif
} }
@ -2726,7 +2724,13 @@ qboolean SV_SendClientDatagram (client_t *client)
// send the datagram // send the datagram
sentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client)); sentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client));
if (ISQWCLIENT(client) || ISNQCLIENT(client)) if (ISNQCLIENT(client))
{
client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK];
frame->packetsizeout += sentbytes;
frame->senttime = realtime;
}
else if (ISQWCLIENT(client))
{ {
client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK]; client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK];
frame->packetsizeout += sentbytes; frame->packetsizeout += sentbytes;
@ -2886,6 +2890,11 @@ void SV_UpdateToReliableMessages (void)
SV_BroadcastUserinfoChange(host_client, true, "*bothcolours", NULL); SV_BroadcastUserinfoChange(host_client, true, "*bothcolours", NULL);
} }
} }
if (host_client->dp_ping)
*host_client->dp_ping = SV_CalcPing (host_client, false);
if (host_client->dp_pl)
*host_client->dp_pl = host_client->lossage;
#endif #endif
name = PR_GetString(svprogfuncs, host_client->edict->v->netname); name = PR_GetString(svprogfuncs, host_client->edict->v->netname);

View file

@ -55,7 +55,7 @@ cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The inter
#endif #endif
cvar_t sv_playermodelchecks = CVAR("sv_playermodelchecks", "0"); cvar_t sv_playermodelchecks = CVAR("sv_playermodelchecks", "0");
cvar_t sv_ping_ignorepl = CVARD("sv_ping_ignorepl", "0", "If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis."); cvar_t sv_ping_ignorepl = CVARD("sv_ping_ignorepl", "0", "If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis.");
cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "", "Specifies the default protocol to use for new NQ clients. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required."); cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "", "Specifies the default protocol to use for new NQ clients. This is only relevent for clients that do not report their supported protocols. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required.");
cvar_t sv_minpitch = CVARAFD("minpitch", "", "sv_minpitch", CVAR_SERVERINFO, "Assumed to be -70"); cvar_t sv_minpitch = CVARAFD("minpitch", "", "sv_minpitch", CVAR_SERVERINFO, "Assumed to be -70");
cvar_t sv_maxpitch = CVARAFD("maxpitch", "", "sv_maxpitch", CVAR_SERVERINFO, "Assumed to be 80"); cvar_t sv_maxpitch = CVARAFD("maxpitch", "", "sv_maxpitch", CVAR_SERVERINFO, "Assumed to be 80");
@ -464,6 +464,7 @@ void SVNQ_New_f (void)
extern cvar_t sv_listen_nq; extern cvar_t sv_listen_nq;
const char *gamedir; const char *gamedir;
unsigned int modelcount, soundcount; unsigned int modelcount, soundcount;
extern cvar_t allow_download;
host_client->prespawn_stage = PRESPAWN_INVALID; host_client->prespawn_stage = PRESPAWN_INVALID;
host_client->prespawn_idx = 0; host_client->prespawn_idx = 0;
@ -608,17 +609,19 @@ void SVNQ_New_f (void)
SV_LogPlayer(host_client, "new (DP6)"); SV_LogPlayer(host_client, "new (DP6)");
protmain = PROTOCOL_VERSION_DP6; protmain = PROTOCOL_VERSION_DP6;
protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things
protext2 = host_client->fteprotocolextensions2 = host_client->fteprotocolextensions2 & ~(PEXT2_PREDINFO|PEXT2_REPLACEMENTDELTAS); //always disabled. kinda interferes with expectations.
protoname = "DPP6"; protoname = "DPP6";
break; break;
case SCP_DARKPLACES7: case SCP_DARKPLACES7:
SV_LogPlayer(host_client, "new (DP7)"); SV_LogPlayer(host_client, "new (DP7)");
protmain = PROTOCOL_VERSION_DP7; protmain = PROTOCOL_VERSION_DP7;
protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things
protext2 = host_client->fteprotocolextensions2 = host_client->fteprotocolextensions2 & ~(PEXT2_PREDINFO|PEXT2_REPLACEMENTDELTAS); //always disabled. kinda interferes with expectations.
protoname = "DPP7"; protoname = "DPP7";
break; break;
default: default:
host_client->drop = true; host_client->drop = true;
protoname = "?""?""?"; protoname = "Unknown";
break; break;
} }
@ -654,9 +657,17 @@ void SVNQ_New_f (void)
//note that those clients will also glitch out from vanilla servers too. //note that those clients will also glitch out from vanilla servers too.
//vanilla prints something like: VERSION 1.08 SERVER (%i CRC) //vanilla prints something like: VERSION 1.08 SERVER (%i CRC)
//which isn't all that useful. so lets customise it to advertise properly, as well as provide gamedir and map (file)name info //which isn't all that useful. so lets customise it to advertise properly, as well as provide gamedir and map (file)name info
Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (%s%s%s%s %s) - %s", 2, gamedir, if (protext2 & PEXT2_REPLACEMENTDELTAS)
protoname,(protext1||(protext2&~(PEXT2_REPLACEMENTDELTAS|PEXT2_VOICECHAT)))?"+":"",(protext2&PEXT2_REPLACEMENTDELTAS)?"F":"",(protext2&PEXT2_VOICECHAT)?"V":"", {
build, mapname); Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (FTENQ, %s) - %s", 2, gamedir,
build, mapname);
}
else
{
Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (%s%s%s, %s) - %s", 2, gamedir,
protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"",
build, mapname);
}
MSG_WriteByte (&host_client->netchan.message, svc_print); MSG_WriteByte (&host_client->netchan.message, svc_print);
MSG_WriteString (&host_client->netchan.message,message); MSG_WriteString (&host_client->netchan.message,message);
} }
@ -664,14 +675,10 @@ void SVNQ_New_f (void)
if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7) if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)
{ {
size_t sz; size_t sz;
extern cvar_t allow_download;
char *f; char *f;
if (allow_download.value) MSG_WriteByte (&host_client->netchan.message, svc_stufftext);
{ MSG_WriteString (&host_client->netchan.message, "cl_serverextension_download 1\n");
MSG_WriteByte (&host_client->netchan.message, svc_stufftext);
MSG_WriteString (&host_client->netchan.message, "cl_serverextension_download 1\n");
}
f = COM_LoadTempFile("csprogs.dat", &sz); f = COM_LoadTempFile("csprogs.dat", &sz);
if (f) if (f)
@ -687,6 +694,12 @@ void SVNQ_New_f (void)
MSG_WriteString (&host_client->netchan.message, "cmd enablecsqc\n"); MSG_WriteString (&host_client->netchan.message, "cmd enablecsqc\n");
} }
} }
else if (allow_download.value && (protext1||protext2))
{ //technically this is a DP extension, but is separate from actual protocols and shouldn't harm anything.
//it is annoying to have prints about unknown commands however, hence the above pext checks (which are unfortunate).
MSG_WriteByte (&host_client->netchan.message, svc_stufftext);
MSG_WriteString (&host_client->netchan.message, "cl_serverextension_download 1\n");
}
MSG_WriteByte (&host_client->netchan.message, svc_serverdata); MSG_WriteByte (&host_client->netchan.message, svc_serverdata);
if (protext1) if (protext1)
@ -1873,6 +1886,32 @@ void SV_Begin_Core(client_t *split)
} }
else else
{ {
#ifndef NOLEGACY
sv_player->xv->clientcolors = host_client->playercolor;
if (progstype != PROG_QW)
{ //some redundant things, purely for dp compat
eval_t *eval;
edict_t *ent = split->edict;
sv_player->v->team = host_client->playercolor&15;
eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, "playermodel", ev_string, NULL);
if (eval)
svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, Info_ValueForKey(split->userinfo, "model"), false);
eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, "playerskin", ev_string, NULL);
if (eval)
svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, Info_ValueForKey(split->userinfo, "skin"), false);
eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, "netaddress", ev_string, NULL);
if (eval)
{
char buf[256];
svprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, NET_AdrToString(buf, sizeof(buf), &split->netchan.remote_address), false);
}
}
#endif
if (split->spectator) if (split->spectator)
{ {
SV_SpawnSpectator (); SV_SpawnSpectator ();
@ -1986,6 +2025,14 @@ void SV_Begin_Core(client_t *split)
} }
} }
} }
split->dp_ping = NULL;
split->dp_pl = NULL;
if (progstype == PROG_NQ)
{
split->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping", ev_float, NULL);
split->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping_packetloss", ev_float, NULL);
}
} }
/* /*
@ -4184,11 +4231,6 @@ void SV_SetInfo_f (void)
// SV_FullClientUpdate (host_client, &sv.reliable_datagram); // SV_FullClientUpdate (host_client, &sv.reliable_datagram);
// host_client->sendinfo = true; // host_client->sendinfo = true;
if (progstype != PROG_QW && !strcmp(key, "bottomcolor"))
{ //team fortress has a nasty habit of booting people without this
sv_player->v->team = atoi(Cmd_Argv(2))+1;
}
if (!strcmp(Info_ValueForKey(host_client->userinfo, key), oldval)) if (!strcmp(Info_ValueForKey(host_client->userinfo, key), oldval))
return; // key hasn't changed return; // key hasn't changed
@ -4201,6 +4243,25 @@ void SV_SetInfo_f (void)
} }
#endif #endif
if (progstype != PROG_QW && !strcmp(key, "bottomcolor"))
{ //team fortress has a nasty habit of booting people without this
sv_player->v->team = atoi(Cmd_Argv(2))+1;
}
#ifndef NOLEGACY
if (progstype != PROG_QW && !strcmp(key, "model"))
{
eval_t *eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, sv_player, "playermodel", ev_string, NULL);
if (eval)
svprogfuncs->SetStringField(svprogfuncs, sv_player, &eval->string, Cmd_Argv(2), false);
}
if (progstype != PROG_QW && !strcmp(key, "skin"))
{
eval_t *eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, sv_player, "playerskin", ev_string, NULL);
if (eval)
svprogfuncs->SetStringField(svprogfuncs, sv_player, &eval->string, Cmd_Argv(2), false);
}
#endif
// process any changed values // process any changed values
SV_ExtractFromUserinfo (host_client, true); SV_ExtractFromUserinfo (host_client, true);
@ -4801,6 +4862,14 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
bc = 0; bc = 0;
ent->xv->clientcolors = 16*tc + bc; ent->xv->clientcolors = 16*tc + bc;
} }
cl->dp_ping = NULL;
cl->dp_pl = NULL;
if (progstype == PROG_NQ)
{
cl->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, ent, "ping", ev_float, NULL);
cl->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, ent, "ping_packetloss", ev_float, NULL);
}
#endif #endif
@ -5541,6 +5610,17 @@ static void SVNQ_NQColour_f (void)
SV_ExtractFromUserinfo (host_client, true); SV_ExtractFromUserinfo (host_client, true);
} }
static void SVNQ_DPModel_f (void)
{
Cmd_TokenizeString(va("setinfo model \"%s\"\n", Cmd_Argv(1)), false, false);
SV_SetInfo_f();
}
static void SVNQ_DPSkin_f (void)
{
Cmd_TokenizeString(va("setinfo skin \"%s\"\n", Cmd_Argv(1)), false, false);
SV_SetInfo_f();
}
static void SVNQ_Ping_f(void) static void SVNQ_Ping_f(void)
{ {
int i; int i;
@ -5597,9 +5677,16 @@ static void SVNQ_Status_f(void)
SV_PrintToClient(host_client, PRINT_HIGH, va("players: %i active (%i max)\n\n", count, min(maxclients.ival+maxspectators.ival,sv.allocated_client_slots)));//must be last SV_PrintToClient(host_client, PRINT_HIGH, va("players: %i active (%i max)\n\n", count, min(maxclients.ival+maxspectators.ival,sv.allocated_client_slots)));//must be last
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++) for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
{ {
int hours, mins, secs;
if (!cl->state) if (!cl->state)
continue; continue;
SV_PrintToClient(host_client, PRINT_HIGH, va("#%i\n", i+1)); secs = realtime - cl->connection_started;
mins = secs/60;
secs -= mins*60;
hours = mins/60;
mins -= hours*60;
SV_PrintToClient(host_client, PRINT_HIGH, va("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, cl->name, cl->old_frags, hours, mins, secs));
SV_PrintToClient(host_client, PRINT_HIGH, va(" %s\n", SV_PlayerPublicAddress(cl))); SV_PrintToClient(host_client, PRINT_HIGH, va(" %s\n", SV_PlayerPublicAddress(cl)));
} }
} }
@ -5887,8 +5974,8 @@ ucmd_t nqucmds[] =
{"setinfo", SV_SetInfo_f}, {"setinfo", SV_SetInfo_f},
{"name", SVNQ_NQInfo_f}, {"name", SVNQ_NQInfo_f},
{"color", SVNQ_NQColour_f}, {"color", SVNQ_NQColour_f},
{"playermodel", NULL}, {"playermodel", SVNQ_DPModel_f},
{"playerskin", NULL}, {"playerskin", SVNQ_DPSkin_f},
{"rate", SV_Rate_f}, {"rate", SV_Rate_f},
{"rate_burstsize", NULL}, {"rate_burstsize", NULL},
@ -6146,7 +6233,7 @@ float V_CalcRoll (vec3_t angles, vec3_t velocity)
vec3_t pmove_mins, pmove_maxs; vec3_t pmove_mins, pmove_maxs;
static qboolean AddEntityToPmove(edict_t *player, edict_t *check) static qboolean AddEntityToPmove(world_t *w, wedict_t *player, wedict_t *check)
{ {
physent_t *pe; physent_t *pe;
int solid = check->v->solid; int solid = check->v->solid;
@ -6163,7 +6250,7 @@ static qboolean AddEntityToPmove(edict_t *player, edict_t *check)
pmove.numphysent++; pmove.numphysent++;
VectorCopy (check->v->origin, pe->origin); VectorCopy (check->v->origin, pe->origin);
pe->info = NUM_FOR_EDICT(svprogfuncs, check); pe->info = NUM_FOR_EDICT(w->progs, check);
pe->nonsolid = solid == SOLID_TRIGGER; pe->nonsolid = solid == SOLID_TRIGGER;
pe->isportal = solid == SOLID_PORTAL; pe->isportal = solid == SOLID_PORTAL;
q1contents = (int)check->v->skin; q1contents = (int)check->v->skin;
@ -6215,28 +6302,30 @@ static qboolean AddEntityToPmove(edict_t *player, edict_t *check)
} }
return true; return true;
} }
/*
====================
AddLinksToPmove
==================== #if 1
*/ #ifdef USEAREAGRID
void AddLinksToPmove ( edict_t *player, areanode_t *node ) extern size_t areagridsequence;
static void AddLinksToPmove (world_t *w, wedict_t *player, areagridlink_t *node)
{ {
int Q1_HullPointContents (hull_t *hull, int num, vec3_t p); int Q1_HullPointContents (hull_t *hull, int num, vec3_t p);
link_t *l, *next; link_t *l, *next;
edict_t *check; wedict_t *check;
int pl; int pl;
int i; int i;
int solid; int solid;
pl = EDICT_TO_PROG(svprogfuncs, player); pl = EDICT_TO_PROG(w->progs, player);
// touch linked edicts // touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next) for (l = node->l.next ; l != &node->l ; l = next)
{ {
next = l->next; next = l->next;
check = (edict_t*)EDICT_FROM_AREA(l); check = ((areagridlink_t*)l)->ed;
if (check->gridareasequence == areagridsequence)
continue;
check->gridareasequence = areagridsequence;
if (check->v->owner == pl) if (check->v->owner == pl)
continue; // player's own missile continue; // player's own missile
@ -6261,38 +6350,33 @@ void AddLinksToPmove ( edict_t *player, areanode_t *node )
if (i != 3) if (i != 3)
continue; continue;
if (!AddEntityToPmove(player, check)) if (!AddEntityToPmove(w, player, check))
break; break;
} }
} }
// recurse down both sides
if (node->axis == -1)
return;
if (pmove_maxs[node->axis] > node->dist)
AddLinksToPmove (player, node->children[0]);
if (pmove_mins[node->axis] < node->dist)
AddLinksToPmove (player, node->children[1]);
} }
//ignores mins/maxs. //ignores mins/maxs.
//portals are expected to be weird. player movement code is nasty. //portals are expected to be weird. player movement code is nasty.
void AddLinksToPmove_Force ( edict_t *player, areanode_t *node ) static void AddPortalsToPmove (world_t *w, wedict_t *player, areagridlink_t *node)
{ {
link_t *l, *next; link_t *l, *next;
edict_t *check; wedict_t *check;
int pl; int pl;
// int i; // int i;
int solid; int solid;
pl = EDICT_TO_PROG(svprogfuncs, player); pl = EDICT_TO_PROG(w->progs, player);
// touch linked edicts // touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next) for (l = node->l.next ; l != &node->l ; l = next)
{ {
next = l->next; next = l->next;
check = (edict_t*)EDICT_FROM_AREA(l); check = ((areagridlink_t*)l)->ed;
if (check->gridareasequence == areagridsequence)
continue;
check->gridareasequence = areagridsequence;
if (check->v->owner == pl) if (check->v->owner == pl)
continue; // player's own missile continue; // player's own missile
@ -6309,7 +6393,73 @@ void AddLinksToPmove_Force ( edict_t *player, areanode_t *node )
//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it //|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it
) )
{ {
if (!AddEntityToPmove(player, check)) if (!AddEntityToPmove(w, player, check))
break;
}
}
}
void AddAllLinksToPmove (world_t *w, wedict_t *player)
{
int ming[2], maxg[2], g[2];
CALCAREAGRIDBOUNDS(w, pmove_mins, pmove_maxs);
areagridsequence++;
AddLinksToPmove(w, player, &w->jumboarea);
for (g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
for (g[1] = ming[1]; g[1] < maxg[1]; g[1]++)
AddLinksToPmove(w, player, &w->gridareas[g[0] + g[1]*w->gridsize[0]]);
AddPortalsToPmove(w, player, &w->portallist);
}
#else
/*
====================
AddLinksToPmove
====================
*/
void AddLinksToPmove (world_t *w, wedict_t *player, areanode_t *node)
{
int Q1_HullPointContents (hull_t *hull, int num, vec3_t p);
link_t *l, *next;
wedict_t *check;
int pl;
int i;
int solid;
pl = EDICT_TO_PROG(w->progs, player);
// touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next)
{
next = l->next;
check = (wedict_t*)EDICT_FROM_AREA(l);
if (check->v->owner == pl)
continue; // player's own missile
if (check == player)
continue;
solid = check->v->solid;
if (
(solid == SOLID_TRIGGER && check->v->skin < 0)
|| solid == SOLID_BSP
|| solid == SOLID_PORTAL
|| solid == SOLID_BBOX
|| solid == SOLID_SLIDEBOX
|| solid == SOLID_LADDER
//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it
)
{
for (i=0 ; i<3 ; i++)
if (check->v->absmin[i] > pmove_maxs[i]
|| check->v->absmax[i] < pmove_mins[i])
break;
if (i != 3)
continue;
if (!AddEntityToPmove(w, player, check))
break; break;
} }
} }
@ -6319,12 +6469,61 @@ void AddLinksToPmove_Force ( edict_t *player, areanode_t *node )
return; return;
if (pmove_maxs[node->axis] > node->dist) if (pmove_maxs[node->axis] > node->dist)
AddLinksToPmove_Force (player, node->children[0]); AddLinksToPmove (w, player, node->children[0]);
if (pmove_mins[node->axis] < node->dist) if (pmove_mins[node->axis] < node->dist)
AddLinksToPmove_Force (player, node->children[1]); AddLinksToPmove (w, player, node->children[1]);
} }
//ignores mins/maxs.
//portals are expected to be weird. player movement code is nasty.
void AddLinksToPmove_Force (world_t *w, wedict_t *player, areanode_t *node)
{
link_t *l, *next;
wedict_t *check;
int pl;
// int i;
int solid;
pl = EDICT_TO_PROG(w->progs, player);
// touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next)
{
next = l->next;
check = (wedict_t*)EDICT_FROM_AREA(l);
if (check->v->owner == pl)
continue; // player's own missile
if (check == player)
continue;
solid = check->v->solid;
if (
(solid == SOLID_TRIGGER && check->v->skin < 0)
|| solid == SOLID_BSP
|| solid == SOLID_PORTAL
|| solid == SOLID_BBOX
|| solid == SOLID_SLIDEBOX
|| solid == SOLID_LADDER
//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it
)
{
if (!AddEntityToPmove(w, player, check))
break;
}
}
// recurse down both sides
if (node->axis == -1)
return;
if (pmove_maxs[node->axis] > node->dist)
AddLinksToPmove_Force (w, player, node->children[0]);
if (pmove_mins[node->axis] < node->dist)
AddLinksToPmove_Force (w, player, node->children[1]);
}
#endif
#else
/* /*
================ ================
AddAllEntsToPmove AddAllEntsToPmove
@ -6332,17 +6531,17 @@ AddAllEntsToPmove
For debugging For debugging
================ ================
*/ */
void AddAllEntsToPmove (edict_t *player) void AddAllEntsToPmove (wedict_t *player, world_t *w)
{ {
int e; int e;
edict_t *check; wedict_t *check;
int i; int i;
int pl; int pl;
pl = EDICT_TO_PROG(svprogfuncs, player); pl = EDICT_TO_PROG(w->progs, player);
for (e=1 ; e<sv.world.num_edicts ; e++) for (e=1 ; e<w->num_edicts ; e++)
{ {
check = EDICT_NUM(svprogfuncs, e); check = EDICT_NUM(w->progs, e);
if (ED_ISFREE(check)) if (ED_ISFREE(check))
continue; continue;
if (check->v->owner == pl) if (check->v->owner == pl)
@ -6366,6 +6565,7 @@ void AddAllEntsToPmove (edict_t *player)
} }
} }
} }
#endif
int SV_PMTypeForClient (client_t *cl, edict_t *ent) int SV_PMTypeForClient (client_t *cl, edict_t *ent)
{ {
@ -6483,7 +6683,14 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
if (ucmd->msec && host_client->msecs > 500) if (ucmd->msec && host_client->msecs > 500)
host_client->msecs = 500; host_client->msecs = 500;
if (ucmd->msec > host_client->msecs) if (ucmd->msec > host_client->msecs)
return; { //they're over their timeslice allocation
//if they're not taking the piss then be prepared to truncate the frame. this should hide clockskew without allowing full-on speedcheats.
if (ucmd->msec > 10)
ucmd->msec -= 1;
if (ucmd->msec > host_client->msecs)
return;
ucmd->msec = host_client->msecs;
}
host_client->msecs -= ucmd->msec; host_client->msecs -= ucmd->msec;
#else #else
// DMW copied this KK hack copied from QuakeForge anti-cheat // DMW copied this KK hack copied from QuakeForge anti-cheat
@ -6852,12 +7059,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
pmove_maxs[i] = pmove.origin[i] + 256; pmove_maxs[i] = pmove.origin[i] + 256;
} }
sv_player->xv->pmove_flags = (int)sv_player->xv->pmove_flags & ~PMF_LADDER; //assume not touching ladder trigger sv_player->xv->pmove_flags = (int)sv_player->xv->pmove_flags & ~PMF_LADDER; //assume not touching ladder trigger
#if 1 AddAllLinksToPmove (&sv.world, (wedict_t*)sv_player);
AddLinksToPmove ( sv_player, sv.world.areanodes );
#else
AddAllEntsToPmove (sv_player);
#endif
AddLinksToPmove_Force ( sv_player, &sv.world.portallist );
if ((int)sv_player->xv->pmove_flags & PMF_LADDER) if ((int)sv_player->xv->pmove_flags & PMF_LADDER)
pmove.onladder = true; pmove.onladder = true;
@ -7949,6 +8151,7 @@ void SVNQ_ReadClientMove (usercmd_t *move)
} }
else else
{ {
host_client->last_sequence = 0; //let the client know that prediction is fucked, by not acking any input frames.
if (i) if (i)
host_client->edict->v->impulse = i; host_client->edict->v->impulse = i;
host_client->isindependant = false; host_client->isindependant = false;
@ -7959,14 +8162,14 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
{ {
int c; int c;
char *s; char *s;
client_frame_t *frame; // client_frame_t *frame;
cl->netchan.outgoing_sequence++; cl->netchan.outgoing_sequence++;
cl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1; cl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1;
// calc ping time // calc ping time
frame = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; // frame = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];
frame->ping_time = -1; // frame->ping_time = -1;
// make sure the reply sequence number matches the incoming // make sure the reply sequence number matches the incoming
// sequence number // sequence number
@ -7976,11 +8179,11 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
// cl->send_message = false; // don't reply, sequences have slipped // cl->send_message = false; // don't reply, sequences have slipped
// save time for ping calculations // save time for ping calculations
cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; // cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime;
cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; // cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1;
cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].move_msecs = -1; // cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].move_msecs = -1;
cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizein = net_message.cursize; // cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizein = net_message.cursize;
cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizeout = 0; // cl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizeout = 0;
host_client = cl; host_client = cl;
sv_player = host_client->edict; sv_player = host_client->edict;
@ -8047,6 +8250,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
if (cl->delta_sequence == -1 && cl->pendingdeltabits) if (cl->delta_sequence == -1 && cl->pendingdeltabits)
cl->pendingdeltabits[0] = UF_REMOVE; cl->pendingdeltabits[0] = UF_REMOVE;
SV_AckEntityFrame(cl, cl->delta_sequence); SV_AckEntityFrame(cl, cl->delta_sequence);
cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].ping_time = realtime - cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].senttime;
break; break;
case clcdp_ackdownloaddata: case clcdp_ackdownloaddata:
SV_DarkPlacesDownloadAck(cl); SV_DarkPlacesDownloadAck(cl);

View file

@ -791,6 +791,7 @@ static model_t *QDECL SVQ2_GetCModel(world_t *w, int modelindex)
void SVQ2_InitWorld(void) void SVQ2_InitWorld(void)
{ {
World_ClearWorld_Nodes (&sv.world, false);
sv.world.Get_CModel = SVQ2_GetCModel; sv.world.Get_CModel = SVQ2_GetCModel;
} }

View file

@ -5,6 +5,9 @@
#ifdef Q3SERVER #ifdef Q3SERVER
#ifndef MAX_ENT_CLUSTERS
#define MAX_ENT_CLUSTERS 16
#endif
#define USEBOTLIB #define USEBOTLIB
@ -119,9 +122,14 @@ const char *mapentspointer;
//these entities are private to the engine. gamecode shall not see this.
typedef struct { typedef struct {
#ifdef USEAREAGRID
areagridlink_t areas[16];
size_t areagridsequence;
#else
link_t area; link_t area;
#endif
qboolean linked; qboolean linked;
int areanum; int areanum;
int areanum2; int areanum2;
@ -144,11 +152,23 @@ static void Q3G_UnlinkEntity(q3sharedEntity_t *ent)
return; // not linked in anywhere return; // not linked in anywhere
} }
#ifdef USEAREAGRID
{int i;
for (i = 0; i < countof(sent->areas); i++)
{
if (!sent->areas[i].ed)
break;
RemoveLink(&sent->areas[i].l);
sent->areas[i].ed = NULL;
}
}
#else
if (sent->area.prev == NULL || sent->area.next == NULL) if (sent->area.prev == NULL || sent->area.next == NULL)
SV_Error("Null entity links in linked entity\n"); SV_Error("Null entity links in linked entity\n");
RemoveLink(&sent->area); RemoveLink(&sent->area);
sent->area.prev = sent->area.next = NULL; sent->area.prev = sent->area.next = NULL;
#endif
sent->linked = false; sent->linked = false;
} }
@ -181,7 +201,11 @@ static model_t *Q3G_GetCModel(unsigned int modelindex)
static void Q3G_LinkEntity(q3sharedEntity_t *ent) static void Q3G_LinkEntity(q3sharedEntity_t *ent)
{ {
#ifdef USEAREAGRID
int ming[2], maxg[2], g[2], ga;
#else
areanode_t *node; areanode_t *node;
#endif
q3serverEntity_t *sent; q3serverEntity_t *sent;
int leafs[MAX_TOTAL_ENT_LEAFS]; int leafs[MAX_TOTAL_ENT_LEAFS];
int clusters[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS];
@ -346,6 +370,23 @@ static void Q3G_LinkEntity(q3sharedEntity_t *ent)
sent->linked = true; sent->linked = true;
// find the first node that the ent's box crosses // find the first node that the ent's box crosses
#ifdef USEAREAGRID
CALCAREAGRIDBOUNDS(&sv.world, ent->r.absmin, ent->r.absmax);
if ((maxg[0]-ming[0])*(maxg[1]-ming[1]) > countof(sent->areas))
{ //entity is too large to fit in our grid. shove it in the overflow
sent->areas[0].ed = sent;
InsertLinkBefore (&sent->areas[0].l, &sv.world.jumboarea.l);
}
else
{
for (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
for ( g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)
{
sent->areas[ga].ed = sent;
InsertLinkBefore (&sent->areas[ga].l, &sv.world.gridareas[g[0] + g[1]*sv.world.gridsize[0]].l);
}
}
#else
node = sv.world.areanodes; node = sv.world.areanodes;
while(1) while(1)
{ {
@ -361,8 +402,53 @@ static void Q3G_LinkEntity(q3sharedEntity_t *ent)
} }
// link it in // link it in
InsertLinkBefore((link_t *)&sent->area, &node->edicts); InsertLinkBefore((link_t *)&sent->area, &node->edicts);
#endif
} }
#ifdef USEAREAGRID
static int SVQ3_EntitiesInBoxNode(areagridlink_t *node, vec3_t mins, vec3_t maxs, int *list, int maxcount)
{
link_t *l, *next;
q3serverEntity_t *sent;
q3sharedEntity_t *gent;
int linkcount = 0;
//work out who they are first.
for (l = node->l.next ; l != &node->l ; l = next)
{
if (maxcount == linkcount)
return linkcount;
next = l->next;
sent = ((areagridlink_t*)l)->ed;
if (sent->areagridsequence != areagridsequence)
{
sent->areagridsequence = areagridsequence;
gent = GENTITY_FOR_SENTITY(sent);
if (!BoundsIntersect(mins, maxs, gent->r.absmin, gent->r.absmax))
continue;
list[linkcount++] = NUM_FOR_GENTITY(gent);
}
}
return linkcount;
}
static int SVQ3_EntitiesInBox(vec3_t mins, vec3_t maxs, int *list, int maxcount)
{
int ming[2], maxg[2], g[2], ga;
int linkcount = 0;
areagridsequence++;
linkcount += SVQ3_EntitiesInBoxNode(&sv.world.jumboarea, mins, maxs, list+linkcount, maxcount-linkcount);
CALCAREAGRIDBOUNDS(&sv.world, mins, maxs);
for (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
for ( g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)
linkcount += SVQ3_EntitiesInBoxNode(&sv.world.gridareas[g[0] + g[1]*sv.world.gridsize[0]], mins, maxs, list+linkcount, maxcount-linkcount);
return linkcount;
}
#else
static int SVQ3_EntitiesInBoxNode(areanode_t *node, vec3_t mins, vec3_t maxs, int *list, int maxcount) static int SVQ3_EntitiesInBoxNode(areanode_t *node, vec3_t mins, vec3_t maxs, int *list, int maxcount)
{ {
link_t *l, *next; link_t *l, *next;
@ -405,6 +491,7 @@ static int SVQ3_EntitiesInBox(vec3_t mins, vec3_t maxs, int *list, int maxcount)
return 0; return 0;
return SVQ3_EntitiesInBoxNode(sv.world.areanodes, mins, maxs, list, maxcount); return SVQ3_EntitiesInBoxNode(sv.world.areanodes, mins, maxs, list, maxcount);
} }
#endif
#define ENTITYNUM_NONE (MAX_GENTITIES-1) #define ENTITYNUM_NONE (MAX_GENTITIES-1)
#define ENTITYNUM_WORLD (MAX_GENTITIES-2) #define ENTITYNUM_WORLD (MAX_GENTITIES-2)

View file

@ -31,6 +31,8 @@ line of sight checks trace->crosscontent, but bullets don't
*/ */
size_t areagridsequence; //used to avoid poking the same ent twice.
extern cvar_t sv_compatiblehulls; extern cvar_t sv_compatiblehulls;
extern cvar_t sv_gameplayfix_nolinknonsolid; extern cvar_t sv_gameplayfix_nolinknonsolid;
@ -209,6 +211,7 @@ ENTITY AREA CHECKING
=============================================================================== ===============================================================================
*/ */
#if defined(Q2SERVER) || !defined(USEAREAGRID)
/* /*
=============== ===============
SV_CreateAreaNode SV_CreateAreaNode
@ -260,10 +263,12 @@ SV_ClearWorld
=============== ===============
*/ */
void World_ClearWorld (world_t *w, qboolean relink) void World_ClearWorld_Nodes (world_t *w, qboolean relink)
{ {
#if !defined(USEAREAGRID)
int i; int i;
wedict_t *ent; wedict_t *ent;
#endif
int maxdepth; int maxdepth;
vec3_t mins, maxs; vec3_t mins, maxs;
if (w->worldmodel) if (w->worldmodel)
@ -278,10 +283,12 @@ void World_ClearWorld (world_t *w, qboolean relink)
} }
World_InitBoxHull (); World_InitBoxHull ();
#if !defined(USEAREAGRID)
memset (&w->portallist, 0, sizeof(w->portallist)); memset (&w->portallist, 0, sizeof(w->portallist));
ClearLink (&w->portallist.edicts); ClearLink (&w->portallist.edicts);
w->portallist.axis = -1; w->portallist.axis = -1;
#endif
maxdepth = 8; maxdepth = 8;
@ -296,7 +303,7 @@ void World_ClearWorld (world_t *w, qboolean relink)
w->numareanodes = 0; w->numareanodes = 0;
World_CreateAreaNode (w, 0, mins, maxs); World_CreateAreaNode (w, 0, mins, maxs);
#if !defined(USEAREAGRID)
if (relink) if (relink)
{ {
for (i=0 ; i<w->num_edicts ; i++) for (i=0 ; i<w->num_edicts ; i++)
@ -310,8 +317,92 @@ void World_ClearWorld (world_t *w, qboolean relink)
World_LinkEdict (w, ent, false); // relink ents so touch functions continue to work. World_LinkEdict (w, ent, false); // relink ents so touch functions continue to work.
} }
} }
#endif
} }
#ifdef USEAREAGRID
static void World_ClearWorld_AreaGrid (world_t *w, qboolean relink)
{
int numareas = 1;
int i, j;
wedict_t *ent;
vec3_t mins, maxs, size;
if (w->worldmodel)
{
VectorCopy(w->worldmodel->mins, mins);
VectorCopy(w->worldmodel->maxs, maxs);
}
else
{
VectorSet(mins, -4096, -4096, -4096);
VectorSet(maxs, 4096, 4096, 4096);
}
Vector2Set(w->gridsize, 128, 128);
for (i = 0; i < 2; i++)
{
size[i] = maxs[i] - mins[i];
size[i] /= w->gridsize[i];
//enforce a minimum grid size, so things don't end up getting added to every single node
if (size[i] < 128)
size[i] = 128;
w->gridscale[i] = size[i];
w->gridbias[i] = -mins[i];
numareas *= w->gridsize[i];
}
World_InitBoxHull ();
if (w->gridareas)
memset (w->gridareas, 0, sizeof(*w->gridareas)*numareas);
else
w->gridareas = Z_Malloc(sizeof(*w->gridareas)*numareas);
for (i = 0; i < numareas; i++)
ClearLink (&w->gridareas[i].l);
ClearLink (&w->jumboarea.l);
ClearLink (&w->portallist.l);
if (relink)
{
for (i=0 ; i<w->num_edicts ; i++)
{
ent = WEDICT_NUM(w->progs, i);
if (!ent)
continue;
for (j = 0; j < countof(ent->gridareas); j++)
{
if (!ent->gridareas[j].l.prev)
break; // not linked in anywhere
ClearLink(&ent->gridareas[j].l);
}
if (ED_ISFREE(ent))
continue;
World_LinkEdict (w, ent, false); // relink ents so touch functions continue to work.
}
}
}
#endif
void World_ClearWorld (world_t *w, qboolean relink)
{
#ifdef Q2SERVER
if (w == &sv.world && svs.gametype == GT_QUAKE2)
World_ClearWorld_Nodes(w, relink);
else
#endif
{
#ifdef USEAREAGRID
World_ClearWorld_AreaGrid(w, relink);
#else
World_ClearWorld_Nodes(w, relink);
#endif
}
}
#endif
#if !defined(USEAREAGRID)
/* /*
=============== ===============
@ -327,16 +418,14 @@ void World_UnlinkEdict (wedict_t *ent)
ent->area.prev = ent->area.next = NULL; ent->area.prev = ent->area.next = NULL;
} }
/* /*
==================== ====================
SV_TouchLinks SV_TouchLinks
==================== ====================
*/ */
#define MAX_NODELINKS 256 //all this means is that any more than this will not touch.
static wedict_t *nodelinks[MAX_NODELINKS];
void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node) void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)
{ {
static wedict_t *nodelinks[256]; //all this means is that any more than this will not touch. probably you won't have that many valid triggers
link_t *l, *next; link_t *l, *next;
wedict_t *touch; wedict_t *touch;
@ -345,7 +434,7 @@ void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)
//work out who they are first. //work out who they are first.
for (l = node->edicts.next ; l != &node->edicts ; l = next) for (l = node->edicts.next ; l != &node->edicts ; l = next)
{ {
if (linkcount == MAX_NODELINKS) if (linkcount == countof(nodelinks))
break; break;
next = l->next; next = l->next;
touch = EDICT_FROM_AREA(l); touch = EDICT_FROM_AREA(l);
@ -406,75 +495,6 @@ void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)
if (ent->v->absmin[node->axis] < node->dist) if (ent->v->absmin[node->axis] < node->dist)
World_TouchLinks (w, ent, node->children[1]); World_TouchLinks (w, ent, node->children[1]);
} }
#if defined(Q2BSPS) || defined(Q3BSPS)
void Q23BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, float *mins, float *maxs)
{
#define MAX_TOTAL_ENT_LEAFS 128
int leafs[MAX_TOTAL_ENT_LEAFS];
int clusters[MAX_TOTAL_ENT_LEAFS];
int num_leafs;
int topnode;
int i, j;
int area;
int nullarea = (model->fromgame == fg_quake2)?0:-1;
//ent->num_leafs == q2's ent->num_clusters
ent->num_leafs = 0;
ent->areanum = nullarea;
ent->areanum2 = nullarea;
if (!mins || !maxs)
return;
//get all leafs, including solids
num_leafs = CM_BoxLeafnums (model, mins, maxs,
leafs, MAX_TOTAL_ENT_LEAFS, &topnode);
// set areas
for (i=0 ; i<num_leafs ; i++)
{
clusters[i] = CM_LeafCluster (model, leafs[i]);
area = CM_LeafArea (model, leafs[i]);
if (area != nullarea)
{ // doors may legally straggle two areas,
// but nothing should ever need more than that
if (ent->areanum != nullarea && ent->areanum != area)
ent->areanum2 = area;
else
ent->areanum = area;
}
}
if (num_leafs >= MAX_TOTAL_ENT_LEAFS)
{ // assume we missed some leafs, and mark by headnode
ent->num_leafs = -1;
ent->headnode = topnode;
}
else
{
ent->num_leafs = 0;
for (i=0 ; i<num_leafs ; i++)
{
if (clusters[i] == -1)
continue; // not a visible leaf
for (j=0 ; j<i ; j++)
if (clusters[j] == clusters[i])
break;
if (j == i)
{
if (ent->num_leafs == MAX_ENT_LEAFS)
{ // assume we missed some leafs, and mark by headnode
ent->num_leafs = -1;
ent->headnode = topnode;
break;
}
ent->leafnums[ent->num_leafs++] = clusters[i];
}
}
}
}
#endif #endif
/* /*
@ -485,10 +505,14 @@ SV_LinkEdict
*/ */
void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
{ {
#ifdef USEAREAGRID
World_UnlinkEdict (ent); // unlink from old position
#else
areanode_t *node; areanode_t *node;
if (ent->area.prev) if (ent->area.prev)
World_UnlinkEdict (ent); // unlink from old position World_UnlinkEdict (ent); // unlink from old position
#endif
if (ent == w->edicts) if (ent == w->edicts)
return; // don't add the world return; // don't add the world
@ -604,6 +628,34 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_nolinknonsolid.ival) if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_nolinknonsolid.ival)
return; return;
#ifdef USEAREAGRID
// find the first node that the ent's box crosses
if (ent->v->solid == SOLID_PORTAL)
{
ent->gridareas[0].ed = ent;
InsertLinkBefore (&ent->gridareas[0].l, &w->portallist.l);
}
else
{
int ming[2], maxg[2], g[2], ga;
CALCAREAGRIDBOUNDS(w, ent->v->absmin, ent->v->absmax);
if ((maxg[0]-ming[0])*(maxg[1]-ming[1]) > countof(ent->gridareas))
{ //entity is too large to fit in our grid. shove it in the overflow
ent->gridareas[0].ed = ent;
InsertLinkBefore (&ent->gridareas[0].l, &w->jumboarea.l);
}
else
{
for (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
for ( g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)
{
ent->gridareas[ga].ed = ent;
InsertLinkBefore (&ent->gridareas[ga].l, &w->gridareas[g[0] + g[1]*w->gridsize[0]].l);
}
}
}
#else
// find the first node that the ent's box crosses // find the first node that the ent's box crosses
if (ent->v->solid == SOLID_PORTAL) if (ent->v->solid == SOLID_PORTAL)
node = &w->portallist; node = &w->portallist;
@ -626,10 +678,11 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
// link it in // link it in
InsertLinkBefore (&ent->area, &node->edicts); InsertLinkBefore (&ent->area, &node->edicts);
#endif
// if touch_triggers, touch all entities at this node and decend for more // if touch_triggers, touch all entities at this node and decend for more
if (touch_triggers && ent->v->solid != SOLID_NOT) if (touch_triggers && ent->v->solid != SOLID_NOT)
World_TouchLinks (w, ent, w->areanodes); World_TouchAllLinks (w, ent);
} }
@ -645,8 +698,8 @@ void VARGS WorldQ2_UnlinkEdict(world_t *w, q2edict_t *ent)
void VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent) void VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent)
{ {
areanode_t *node; areanode_t *node;
int leafs[MAX_TOTAL_ENT_LEAFS]; int leafs[128];
int clusters[MAX_TOTAL_ENT_LEAFS]; int clusters[countof(leafs)];
int num_leafs; int num_leafs;
int i, j; int i, j;
int area; int area;
@ -744,7 +797,7 @@ void VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent)
//get all leafs, including solids //get all leafs, including solids
num_leafs = CM_BoxLeafnums (w->worldmodel, ent->absmin, ent->absmax, num_leafs = CM_BoxLeafnums (w->worldmodel, ent->absmin, ent->absmax,
leafs, MAX_TOTAL_ENT_LEAFS, &topnode); leafs, countof(leafs), &topnode);
// set areas // set areas
for (i=0 ; i<num_leafs ; i++) for (i=0 ; i<num_leafs ; i++)
@ -761,7 +814,7 @@ void VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent)
} }
} }
if (num_leafs >= MAX_TOTAL_ENT_LEAFS) if (num_leafs >= countof(leafs))
{ // assume we missed some leafs, and mark by headnode { // assume we missed some leafs, and mark by headnode
ent->num_clusters = -1; ent->num_clusters = -1;
ent->headnode = topnode; ent->headnode = topnode;
@ -993,6 +1046,79 @@ void WorldQ2_Q1BSP_LinkEdict(world_t *w, q2edict_t *ent)
} }
#endif #endif
#if defined(Q2BSPS) || defined(Q3BSPS)
void Q23BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, float *mins, float *maxs)
{
#define MAX_TOTAL_ENT_LEAFS 128
int leafs[MAX_TOTAL_ENT_LEAFS];
int clusters[MAX_TOTAL_ENT_LEAFS];
int num_leafs;
int topnode;
int i, j;
int area;
int nullarea = (model->fromgame == fg_quake2)?0:-1;
//ent->num_leafs == q2's ent->num_clusters
ent->num_leafs = 0;
ent->areanum = nullarea;
ent->areanum2 = nullarea;
if (!mins || !maxs)
return;
//get all leafs, including solids
num_leafs = CM_BoxLeafnums (model, mins, maxs,
leafs, MAX_TOTAL_ENT_LEAFS, &topnode);
// set areas
for (i=0 ; i<num_leafs ; i++)
{
clusters[i] = CM_LeafCluster (model, leafs[i]);
area = CM_LeafArea (model, leafs[i]);
if (area != nullarea)
{ // doors may legally straggle two areas,
// but nothing should ever need more than that
if (ent->areanum != nullarea && ent->areanum != area)
ent->areanum2 = area;
else
ent->areanum = area;
}
}
if (num_leafs >= MAX_TOTAL_ENT_LEAFS)
{ // assume we missed some leafs, and mark by headnode
ent->num_leafs = -1;
ent->headnode = topnode;
}
else
{
ent->num_leafs = 0;
for (i=0 ; i<num_leafs ; i++)
{
if (clusters[i] == -1)
continue; // not a visible leaf
for (j=0 ; j<i ; j++)
if (clusters[j] == clusters[i])
break;
if (j == i)
{
if (ent->num_leafs == MAX_ENT_LEAFS)
{ // assume we missed some leafs, and mark by headnode
ent->num_leafs = -1;
ent->headnode = topnode;
break;
}
ent->leafnums[ent->num_leafs++] = clusters[i];
}
}
}
}
#endif
/* /*
=============================================================================== ===============================================================================
@ -1180,36 +1306,111 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
return trace; return trace;
} }
#ifdef Q2SERVER
static trace_t WorldQ2_ClipMoveToEntity (world_t *w, q2edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, unsigned int hitcontentsmask) #define AREA_ALL 0
#define AREA_SOLID 1
#define AREA_TRIGGER 2
#ifdef USEAREAGRID
/*
================
SV_AreaEdicts
================
*/
int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype)
{ {
trace_t trace; wedict_t *check;
model_t *model = NULL; areagridlink_t *start, *l;
size_t count = 0;
int ming[2], maxg[2], g[2], ga;
CALCAREAGRIDBOUNDS(w, mins, maxs);
// get the clipping hull areagridsequence++;
if (ent->s.solid == Q2SOLID_BSP)
model = w->Get_CModel(w, ent->s.modelindex);
if (!model || model->type != mod_brush || model->loadstate != MLS_LOADED) //check ents that are just too large first
start = &w->jumboarea;
for (l=(areagridlink_t*)start->l.next ; l != start ; l = (areagridlink_t*)l->l.next)
{ {
vec3_t boxmins, boxmaxs; check = l->ed;
VectorSubtract (ent->mins, maxs, boxmins);
VectorSubtract (ent->maxs, mins, boxmaxs); // if (check->gridareasequence == areagridsequence)
World_HullForBox(boxmins, boxmaxs); // continue;
model = NULL; check->gridareasequence = areagridsequence;
if (areatype != AREA_ALL)
{
if (check->v->solid == SOLID_NOT)
continue; // deactivated
if ((check->v->solid == SOLID_TRIGGER) != (areatype == AREA_TRIGGER))
continue;
}
if (check->v->absmin[0] > maxs[0]
|| check->v->absmin[1] > maxs[1]
|| check->v->absmin[2] > maxs[2]
|| check->v->absmax[0] < mins[0]
|| check->v->absmax[1] < mins[1]
|| check->v->absmax[2] < mins[2])
continue; // not touching
if (count == maxcount)
{
Con_Printf ("World_AreaEdicts: MAXCOUNT\n");
return count;
}
list[count] = check;
count++;
} }
// trace a line through the apropriate clipping hull //check the actual grid now.
World_TransformedTrace(model, 0, 0, start, end, mins, maxs, false, &trace, ent->s.origin, ent->s.angles, hitcontentsmask); for (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
{
for ( g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)
{
start = &w->gridareas[g[0] + g[1]*w->gridsize[0]];
for (l=(areagridlink_t*)start->l.next ; l != start ; l = (areagridlink_t*)l->l.next)
{
check = l->ed;
// did we clip the move? if (check->gridareasequence == areagridsequence)
if (trace.fraction < 1 || trace.startsolid ) continue;
trace.ent = (edict_t *)ent; check->gridareasequence = areagridsequence;
if (areatype != AREA_ALL)
{
if (check->v->solid == SOLID_NOT)
continue; // deactivated
return trace; if ((check->v->solid == SOLID_TRIGGER) != (areatype == AREA_TRIGGER))
continue;
}
if (check->v->absmin[0] > maxs[0]
|| check->v->absmin[1] > maxs[1]
|| check->v->absmin[2] > maxs[2]
|| check->v->absmax[0] < mins[0]
|| check->v->absmax[1] < mins[1]
|| check->v->absmax[2] < mins[2])
continue; // not touching
if (count == maxcount)
{
Con_Printf ("World_AreaEdicts: MAXCOUNT\n");
return count;
}
list[count] = check;
count++;
}
}
}
return count;
} }
#endif
#ifdef Q2BSPS #else
float *area_mins, *area_maxs; float *area_mins, *area_maxs;
wedict_t **area_list; wedict_t **area_list;
#ifdef Q2SERVER #ifdef Q2SERVER
@ -1217,8 +1418,6 @@ q2edict_t **area_q2list;
#endif #endif
int area_count, area_maxcount; int area_count, area_maxcount;
int area_type; int area_type;
#define AREA_SOLID 1
#define AREA_TRIGGER 2
static void World_AreaEdicts_r (areanode_t *node) static void World_AreaEdicts_r (areanode_t *node)
{ {
link_t *l, *next, *start; link_t *l, *next, *start;
@ -1272,8 +1471,7 @@ static void World_AreaEdicts_r (areanode_t *node)
SV_AreaEdicts SV_AreaEdicts
================ ================
*/ */
int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype)
int maxcount, int areatype)
{ {
area_mins = mins; area_mins = mins;
area_maxs = maxs; area_maxs = maxs;
@ -1286,8 +1484,13 @@ int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list,
return area_count; return area_count;
} }
#endif
#ifdef Q2SERVER #ifdef Q2SERVER
float *area_mins, *area_maxs;
q2edict_t **area_q2list;
int area_count, area_maxcount;
int area_type;
static void WorldQ2_AreaEdicts_r (areanode_t *node) static void WorldQ2_AreaEdicts_r (areanode_t *node)
{ {
link_t *l, *next, *start; link_t *l, *next, *start;
@ -1460,7 +1663,6 @@ void WorldQ2_ClipMoveToEntities (world_t *w, moveclip_t *clip )
#undef ped #undef ped
} }
#endif #endif
#endif
//=========================================================================== //===========================================================================
//a portal is flush with a world surface behind it. //a portal is flush with a world surface behind it.
@ -1647,6 +1849,192 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
} }
} }
#ifdef USEAREAGRID
void World_TouchAllLinks (world_t *w, wedict_t *ent)
{
wedict_t *touchedicts[512], *touch;
int num;
num = World_AreaEdicts(w, ent->v->absmin, ent->v->absmax, touchedicts, countof(touchedicts), AREA_TRIGGER);
while (num-- > 0)
{
touch = touchedicts[num];
//make sure nothing moved it away
if (ED_ISFREE(touch))
continue;
if (!touch->v->touch || touch->v->solid != SOLID_TRIGGER)
continue;
if (ent->v->absmin[0] > touch->v->absmax[0]
|| ent->v->absmin[1] > touch->v->absmax[1]
|| ent->v->absmin[2] > touch->v->absmax[2]
|| ent->v->absmax[0] < touch->v->absmin[0]
|| ent->v->absmax[1] < touch->v->absmin[1]
|| ent->v->absmax[2] < touch->v->absmin[2] )
continue;
if (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit)) //didn't change did it?...
continue;
w->Event_Touch(w, touch, ent);
if (ED_ISFREE(ent))
break;
}
}
void World_UnlinkEdict (wedict_t *ent)
{
size_t i;
for (i = 0; i < countof(ent->gridareas); i++)
{
if (!ent->gridareas[i].l.prev)
return; // not linked in anywhere
RemoveLink (&ent->gridareas[i].l);
ent->gridareas[i].l.prev = ent->gridareas[i].l.next = NULL;
}
}
static void World_ClipToLinks (world_t *w, areagridlink_t *node, moveclip_t *clip)
{
link_t *l, *next;
wedict_t *touch;
trace_t trace;
// touch linked edicts
for (l = node->l.next ; l != &node->l ; l = next)
{
next = l->next;
touch = ((areagridlink_t*)l)->ed;
if (touch->gridareasequence == areagridsequence)
continue;
touch->gridareasequence = areagridsequence;
if (touch->v->solid == SOLID_NOT)
continue;
if (touch == clip->passedict)
continue;
/*if its a trigger, we only clip against it if the flags are aligned*/
if (touch->v->solid == SOLID_TRIGGER || touch->v->solid == SOLID_LADDER)
{
if (!(clip->type & MOVE_TRIGGERS))
continue;
if (!((int)touch->v->flags & FL_FINDABLE_NONSOLID))
continue;
}
if (clip->type & MOVE_LAGGED)
{
//can't touch lagged ents - we do an explicit test for them later.
if (touch->entnum-1 < w->maxlagents)
if (w->lagents[touch->entnum-1].present)
continue;
}
if ((clip->type & MOVE_NOMONSTERS) && (touch->v->solid != SOLID_BSP && touch->v->solid != SOLID_PORTAL))
continue;
if (clip->passedict)
{
if (w->usesolidcorpse)
{
#if 1
// if (!(clip->hitcontentsmask & ((touch->v->solid == SOLID_CORPSE)?FTECONTENTS_CORPSE:FTECONTENTS_BODY)))
// continue;
#else
// don't clip corpse against character
if (clip->passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))
continue;
// don't clip character against corpse
if (clip->passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)
continue;
#endif
}
if (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))
continue;
}
if (clip->boxmins[0] > touch->v->absmax[0]
|| clip->boxmins[1] > touch->v->absmax[1]
|| clip->boxmins[2] > touch->v->absmax[2]
|| clip->boxmaxs[0] < touch->v->absmin[0]
|| clip->boxmaxs[1] < touch->v->absmin[1]
|| clip->boxmaxs[2] < touch->v->absmin[2] )
continue;
if (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0])
continue; // points never interact
// might intersect, so do an exact clip
// if (clip->trace.allsolid)
// return;
if (clip->passedict)
{
if ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip->passedict)
continue; // don't clip against own missiles
if ((wedict_t*)PROG_TO_EDICT(w->progs, clip->passedict->v->owner) == touch)
continue; // don't clip against owner
}
if (touch->v->solid == SOLID_PORTAL)
{
//make sure we don't hit the world if we're inside the portal
World_PortalCSG(touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace);
}
if ((int)touch->v->flags & FL_MONSTER)
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
else
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
if (trace.fraction < clip->trace.fraction)
{
//trace traveled less, but don't forget if we started in a solid.
trace.startsolid |= clip->trace.startsolid;
trace.allsolid |= clip->trace.allsolid;
if (clip->type & MOVE_ENTCHAIN)
{
touch->v->chain = EDICT_TO_PROG(w->progs, clip->trace.ent?clip->trace.ent:w->edicts);
clip->trace.ent = touch;
}
else
{
if (clip->trace.startsolid && !trace.startsolid)
trace.ent = clip->trace.ent; //something else hit earlier, that one gets the trace entity, but not the fraction. yeah, combining traces like this was always going to be weird.
else
trace.ent = touch;
clip->trace = trace;
}
}
else if (trace.startsolid || trace.allsolid)
{
//even if the trace traveled less, we still care if it was in a solid.
clip->trace.startsolid |= trace.startsolid;
clip->trace.allsolid |= trace.allsolid;
if (!clip->trace.ent)
clip->trace.ent = touch;
}
}
}
static void World_ClipToAllLinks (world_t *w, moveclip_t *clip)
{
int ming[2], maxg[2], g[2];
areagridsequence++;
World_ClipToLinks(w, &w->jumboarea, clip);
CALCAREAGRIDBOUNDS(w, clip->boxmins, clip->boxmaxs);
for ( g[0] = ming[0]; g[0] < maxg[0]; g[0]++)
for (g[1] = ming[1]; g[1] < maxg[1]; g[1]++)
{
World_ClipToLinks(w, &w->gridareas[g[0] + g[1]*w->gridsize[0]], clip);
}
}
#else
/* /*
==================== ====================
SV_ClipToLinks SV_ClipToLinks
@ -1773,69 +2161,6 @@ static void World_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip)
} }
} }
// recurse down both sides
if (node->axis == -1)
return;
if ( clip->boxmaxs[node->axis] > node->dist )
World_ClipToLinks (w, node->children[0], clip );
if ( clip->boxmins[node->axis] < node->dist )
World_ClipToLinks (w, node->children[1], clip );
}
#ifdef Q2SERVER
static void WorldQ2_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip)
{
link_t *l, *next;
q2edict_t *touch;
trace_t trace;
// touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next)
{
next = l->next;
touch = Q2EDICT_FROM_AREA(l);
if (touch->s.solid == Q2SOLID_NOT)
continue;
if (touch == clip->q2passedict)
continue;
if (touch->s.solid == Q2SOLID_TRIGGER)
SV_Error ("Trigger in clipping list");
if (clip->type & MOVE_NOMONSTERS && touch->s.solid != Q2SOLID_BSP)
continue;
if (clip->boxmins[0] > touch->absmax[0]
|| clip->boxmins[1] > touch->absmax[1]
|| clip->boxmins[2] > touch->absmax[2]
|| clip->boxmaxs[0] < touch->absmin[0]
|| clip->boxmaxs[1] < touch->absmin[1]
|| clip->boxmaxs[2] < touch->absmin[2] )
continue;
if (clip->q2passedict && clip->q2passedict->size[0] && !touch->size[0])
continue; // points never interact
// might intersect, so do an exact clip
if (clip->trace.allsolid)
return;
if (clip->passedict)
{
if (touch->owner == clip->q2passedict)
continue; // don't clip against own missiles
if (clip->q2passedict->owner == touch)
continue; // don't clip against owner
}
trace = WorldQ2_ClipMoveToEntity (w, touch, clip->start, clip->mins, clip->maxs, clip->end, clip->hitcontentsmask);
if (trace.allsolid || trace.startsolid ||
trace.fraction < clip->trace.fraction)
{
trace.ent = (edict_t *)touch;
clip->trace = trace;
}
}
// recurse down both sides // recurse down both sides
if (node->axis == -1) if (node->axis == -1)
return; return;
@ -2127,7 +2452,11 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
wedict_t *touch; wedict_t *touch;
vec3_t lp; vec3_t lp;
World_ClipToLinks (w, w->areanodes, &clip ); #ifdef USEAREAGRID
World_ClipToAllLinks (w, &clip);
#else
World_ClipToLinks (w, w->areanodes, &clip);
#endif
for (i = 0; i < w->maxlagents; i++) for (i = 0; i < w->maxlagents; i++)
{ {
@ -2206,7 +2535,13 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
} }
} }
else else
{
#ifdef USEAREAGRID
World_ClipToAllLinks (w, &clip );
#else
World_ClipToLinks (w, w->areanodes, &clip ); World_ClipToLinks (w, w->areanodes, &clip );
#endif
}
World_ClipToLinks(w, &w->portallist, &clip); World_ClipToLinks(w, &w->portallist, &clip);
} }
@ -2249,12 +2584,7 @@ trace_t WorldQ2_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t
World_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); World_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );
// clip to entities // clip to entities
#ifdef Q2BSPS WorldQ2_ClipMoveToEntities(w, &clip);
if (w->worldmodel->fromgame == fg_quake2 || w->worldmodel->fromgame == fg_quake3)
WorldQ2_ClipMoveToEntities(w, &clip);
else
#endif
WorldQ2_ClipToLinks (w, w->areanodes, &clip );
return clip.trace; return clip.trace;
} }
@ -2320,9 +2650,13 @@ void World_Destroy(world_t *world)
{ {
World_RBE_Shutdown(world); World_RBE_Shutdown(world);
#ifdef USEAREAGRID
Z_Free(world->gridareas);
#else
Z_Free(world->areanodes); Z_Free(world->areanodes);
world->areanodes = NULL; world->areanodes = NULL;
world->areanodedepth = 0; world->areanodedepth = 0;
#endif
memset(world, 0, sizeof(*world)); memset(world, 0, sizeof(*world));
} }

View file

@ -2,7 +2,7 @@ CC=i686-pc-mingw32-gcc
all: all:
NAMES= fixedemu altwater bloom_blur bloom_filter bloom_final colourtint crepuscular_opaque crepuscular_rays crepuscular_sky depthonly default2d defaultadditivesprite defaultskin defaultsky defaultfill defaultsprite defaultwall defaultwarp defaultgammacb drawflat_wall lpp_depthnorm lpp_light lpp_wall postproc_fisheye postproc_panorama postproc_laea postproc_stereographic postproc_equirectangular fxaa underwaterwarp menutint terrain rtlight NAMES= fixedemu altwater bloom_blur bloom_filter bloom_final colourtint crepuscular_opaque crepuscular_rays crepuscular_sky depthonly default2d defaultadditivesprite defaultskin defaultsky defaultskybox defaultfill defaultsprite defaultwall defaultwarp defaultgammacb drawflat_wall lpp_depthnorm lpp_light lpp_wall postproc_fisheye postproc_panorama postproc_laea postproc_stereographic postproc_equirectangular fxaa underwaterwarp menutint terrain rtlight
ALLNAMES+=$(foreach v,$(NAMES),glsl/$v.glsl) ALLNAMES+=$(foreach v,$(NAMES),glsl/$v.glsl)

View file

@ -18,6 +18,7 @@ char shaders[][64] =
"defaultadditivesprite", "defaultadditivesprite",
"defaultskin", "defaultskin",
"defaultsky", "defaultsky",
"defaultskybox",
"defaultfill", "defaultfill",
"defaultsprite", "defaultsprite",
"defaultwall", "defaultwall",

View file

@ -6,6 +6,7 @@
!!permu SKELETAL !!permu SKELETAL
!!permu FOG !!permu FOG
!!permu BUMP !!permu BUMP
!!permu REFLECTCUBEMASK
!!cvarf r_glsl_offsetmapping_scale !!cvarf r_glsl_offsetmapping_scale
!!cvarf gl_specular !!cvarf gl_specular
!!cvardf gl_affinemodels=0 !!cvardf gl_affinemodels=0
@ -36,9 +37,12 @@
affine varying vec2 tc; affine varying vec2 tc;
varying vec3 light; varying vec3 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
varying vec3 eyevector; varying vec3 eyevector;
#endif #endif
#ifdef REFLECTCUBEMASK
varying mat3 invsurface;
#endif
#ifdef TESS #ifdef TESS
varying vec3 vertex; varying vec3 vertex;
varying vec3 normal; varying vec3 normal;
@ -46,7 +50,7 @@ varying vec3 normal;
void main () void main ()
{ {
#if defined(SPECULAR)||defined(OFFSETMAPPING) #if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
vec3 n, s, t, w; vec3 n, s, t, w;
gl_Position = skeletaltransform_wnst(w,n,s,t); gl_Position = skeletaltransform_wnst(w,n,s,t);
vec3 eyeminusvertex = e_eyepos - w.xyz; vec3 eyeminusvertex = e_eyepos - w.xyz;
@ -57,6 +61,11 @@ void main ()
vec3 n, s, t, w; vec3 n, s, t, w;
gl_Position = skeletaltransform_wnst(w,n,s,t); gl_Position = skeletaltransform_wnst(w,n,s,t);
#endif #endif
#ifdef REFLECTCUBEMASK
invsurface[0] = s;
invsurface[1] = t;
invsurface[2] = n;
#endif
tc = v_texcoord; tc = v_texcoord;
@ -92,10 +101,14 @@ affine in vec2 tc[];
affine out vec2 t_tc[]; affine out vec2 t_tc[];
in vec3 light[]; in vec3 light[];
out vec3 t_light[]; out vec3 t_light[];
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
in vec3 eyevector[]; in vec3 eyevector[];
out vec3 t_eyevector[]; out vec3 t_eyevector[];
#endif #endif
#ifdef REFLECTCUBEMASK
in mat3 invsurface[];
out mat3 t_invsurface[];
#endif
void main() void main()
{ {
//the control shader needs to pass stuff through //the control shader needs to pass stuff through
@ -104,9 +117,14 @@ void main()
t_normal[id] = normal[id]; t_normal[id] = normal[id];
t_tc[id] = tc[id]; t_tc[id] = tc[id];
t_light[id] = light[id]; t_light[id] = light[id];
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
t_eyevector[id] = eyevector[id]; t_eyevector[id] = eyevector[id];
#endif #endif
#ifdef REFLECTCUBEMASK
t_invsurface[id][0] = invsurface[id][0];
t_invsurface[id][1] = invsurface[id][1];
t_invsurface[id][2] = invsurface[id][2];
#endif
gl_TessLevelOuter[0] = float(r_tessellation_level); gl_TessLevelOuter[0] = float(r_tessellation_level);
gl_TessLevelOuter[1] = float(r_tessellation_level); gl_TessLevelOuter[1] = float(r_tessellation_level);
@ -132,10 +150,14 @@ affine in vec2 t_tc[];
affine out vec2 tc; affine out vec2 tc;
in vec3 t_light[]; in vec3 t_light[];
out vec3 light; out vec3 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
in vec3 t_eyevector[]; in vec3 t_eyevector[];
out vec3 eyevector; out vec3 eyevector;
#endif #endif
#ifdef REFLECTCUBEMASK
in mat3 t_invsurface[];
out mat3 invsurface;
#endif
#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2]) #define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])
void main() void main()
@ -151,9 +173,14 @@ void main()
//FIXME: we should be recalcing these here, instead of just lerping them //FIXME: we should be recalcing these here, instead of just lerping them
light = LERP(t_light); light = LERP(t_light);
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
eyevector = LERP(t_eyevector); eyevector = LERP(t_eyevector);
#endif #endif
#ifdef REFLECTCUBEMASK
invsurface[0] = LERP(t_invsurface[0]);
invsurface[1] = LERP(t_invsurface[1]);
invsurface[2] = LERP(t_invsurface[2]);
#endif
gl_Position = m_modelviewprojection * vec4(w,1.0); gl_Position = m_modelviewprojection * vec4(w,1.0);
} }
@ -187,9 +214,12 @@ uniform sampler2D s_colourmap;
affine varying vec2 tc; affine varying vec2 tc;
varying vec3 light; varying vec3 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
varying vec3 eyevector; varying vec3 eyevector;
#endif #endif
#ifdef REFLECTCUBEMASK
varying mat3 invsurface;
#endif
void main () void main ()
@ -227,8 +257,17 @@ void main ()
vec4 specs = texture2D(s_specular, tc); vec4 specs = texture2D(s_specular, tc);
vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0)); vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0));
float spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a); float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a);
col.rgb += cvar_gl_specular * spec * specs.rgb; col.rgb += FTE_SPECULAR_MULTIPLIER * spec * specs.rgb;
#elif defined(REFLECTCUBEMASK)
vec3 bumps = vec3(0, 0, 1);
#endif
#ifdef REFLECTCUBEMASK
vec3 rtc = reflect(-eyevector, bumps);
rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];
rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;
col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;
#endif #endif
col.rgb *= light; col.rgb *= light;
@ -236,7 +275,7 @@ void main ()
#ifdef FULLBRIGHT #ifdef FULLBRIGHT
vec4 fb = texture2D(s_fullbright, tc); vec4 fb = texture2D(s_fullbright, tc);
// col.rgb = mix(col.rgb, fb.rgb, fb.a); // col.rgb = mix(col.rgb, fb.rgb, fb.a);
col.rgb += fb.rgb * fb.a; col.rgb += fb.rgb * fb.a * e_glowmod.rgb;
#endif #endif
#endif #endif

View file

@ -0,0 +1,23 @@
!!permu FOG
!!samps reflectcube
#include "sys/defs.h"
#include "sys/fog.h"
//simple shader for simple skyboxes.
varying vec3 pos;
#ifdef VERTEX_SHADER
void main ()
{
pos = v_position.xyz - e_eyepos;
pos.y = -pos.y;
gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
void main ()
{
vec4 skybox = textureCube(s_reflectcube, pos);
gl_FragColor = vec4(fog3(skybox.rgb), 1.0);
}
#endif

View file

@ -24,7 +24,7 @@
varying vec3 eyevector; varying vec3 eyevector;
#endif #endif
#ifdef REFLECTCUBEMASK #if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)
varying mat3 invsurface; varying mat3 invsurface;
#endif #endif
@ -54,7 +54,7 @@ void main ()
eyevector.y = dot(eyeminusvertex, v_tvector.xyz); eyevector.y = dot(eyeminusvertex, v_tvector.xyz);
eyevector.z = dot(eyeminusvertex, v_normal.xyz); eyevector.z = dot(eyeminusvertex, v_normal.xyz);
#endif #endif
#ifdef REFLECTCUBEMASK #if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)
invsurface[0] = v_svector; invsurface[0] = v_svector;
invsurface[1] = v_tvector; invsurface[1] = v_tvector;
invsurface[2] = v_normal; invsurface[2] = v_normal;
@ -275,8 +275,10 @@ void main ()
#else #else
vec3 lightmaps = vc.rgb; vec3 lightmaps = vc.rgb;
#endif #endif
#define delux vec3(0.0,0.0,1.0)
#else #else
#ifdef LIGHTSTYLED #ifdef LIGHTSTYLED
#define delux vec3(0.0,0.0,1.0)
vec3 lightmaps; vec3 lightmaps;
#ifdef DELUXE #ifdef DELUXE
lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxmap0, lm0).rgb-0.5); lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxmap0, lm0).rgb-0.5);
@ -293,9 +295,15 @@ void main ()
vec3 lightmaps = (texture2D(s_lightmap, lm0) * e_lmscale).rgb; vec3 lightmaps = (texture2D(s_lightmap, lm0) * e_lmscale).rgb;
//modulate by the bumpmap dot light //modulate by the bumpmap dot light
#ifdef DELUXE #ifdef DELUXE
vec3 delux = 2.0*(texture2D(s_deluxmap, lm0).rgb-0.5); vec3 delux = (texture2D(s_deluxmap, lm0).rgb-0.5);
lightmaps *= 1.0 / max(0.25, delux.z); //counter the darkening from deluxmaps #ifdef BUMPMODELSPACE
delux = normalize(delux*invsurface);
#else
lightmaps *= 2.0 / max(0.25, delux.z); //counter the darkening from deluxmaps
#endif
lightmaps *= dot(norm, delux); lightmaps *= dot(norm, delux);
#else
#define delux vec3(0.0,0.0,1.0)
#endif #endif
#endif #endif
#endif #endif
@ -303,14 +311,9 @@ void main ()
//add in specular, if applicable. //add in specular, if applicable.
#ifdef SPECULAR #ifdef SPECULAR
vec4 specs = texture2D(s_specular, tc); vec4 specs = texture2D(s_specular, tc);
#if defined(DELUXE) && !defined(VERTEXLIT) vec3 halfdir = normalize(normalize(eyevector) + delux); //this norm should be the deluxemap info instead
//not lightstyled... float spec = pow(max(dot(halfdir, norm), 0.0), 2.0*FTE_SPECULAR_EXPONENT * specs.a);
vec3 halfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_deluxmap0, lm0).rgb-0.5)); //this norm should be the deluxemap info instead spec *= FTE_SPECULAR_MULTIPLIER;
#else
vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0)); //this norm should be the deluxemap info instead
#endif
float spec = pow(max(dot(halfdir, norm), 0.0), 32.0 * specs.a);
spec *= cvar_gl_specular;
//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool. //NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.
//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway, //As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,
//we default to something that is not garish when the light value is directly infront of every single pixel. //we default to something that is not garish when the light value is directly infront of every single pixel.
@ -345,10 +348,19 @@ void main ()
//entity modifiers //entity modifiers
gl_FragColor = gl_FragColor * e_colourident; gl_FragColor = gl_FragColor * e_colourident;
#if defined(MASK)
#if defined(MASKLT)
if (gl_FragColor.a < MASK)
discard;
#else
if (gl_FragColor.a >= MASK)
discard;
#endif
#endif
//and finally hide it all if we're fogged. //and finally hide it all if we're fogged.
#ifdef FOG #ifdef FOG
gl_FragColor = fog4(gl_FragColor); gl_FragColor = fog4(gl_FragColor);
#endif #endif
} }
#endif #endif

View file

@ -281,7 +281,7 @@ void main ()
#ifdef SPECULAR #ifdef SPECULAR
vec3 halfdir = normalize(normalize(eyevector) + nl); vec3 halfdir = normalize(normalize(eyevector) + nl);
float spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a); float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a)*float(SPECMUL);
diff += l_lightcolourscale.z * spec * specs.rgb; diff += l_lightcolourscale.z * spec * specs.rgb;
#endif #endif

View file

@ -44,7 +44,6 @@ struct v2f
SamplerState SampleType; SamplerState SampleType;
//uniform vec4 e_colourident;
float4 main (v2f inp) : SV_TARGET float4 main (v2f inp) : SV_TARGET
{ {
float4 col; float4 col;
@ -69,10 +68,12 @@ struct v2f
#endif #endif
col.rgb *= inp.light; col.rgb *= inp.light;
//#ifdef FULLBRIGHT //#ifdef FULLBRIGHT
float4 fb = t_fullbright.Sample(SampleType, inp.tc); float4 fb = t_fullbright.Sample(SampleType, inp.tc)*e_glowmod;
col.rgb = lerp(col.rgb, fb.rgb, fb.a); // col.rgb = lerp(col.rgb, fb.rgb, fb.a); //matches vanilla quake...
col.rgb += fb.rgb * fb.a; //but nothing expects it to.
//#endif //#endif
col *= e_colourmod;
// col = fog4(col);
return col; return col;
// return fog4(col * e_colourident);
} }
#endif #endif

View file

@ -0,0 +1,39 @@
!!samps reflectcube
//regular sky shader for scrolling q1 skies
//the sky surfaces are thrown through this as-is.
struct a2v
{
float4 pos: POSITION;
};
struct v2f
{
float4 pos: SV_POSITION;
float3 texc: TEXCOORD0;
};
#include <ftedefs.h>
#ifdef VERTEX_SHADER
v2f main (a2v inp)
{
v2f outp;
outp.pos = mul(m_model, inp.pos);
outp.texc= outp.pos.xyz - v_eyepos;
outp.texc.y = -outp.texc.y;
outp.pos = mul(m_view, outp.pos);
outp.pos = mul(m_projection, outp.pos);
return outp;
}
#endif
#ifdef FRAGMENT_SHADER
TextureCube t_reflectcube : register(t0);
SamplerState s_reflectcube : register(s0);
float4 main (v2f inp) : SV_TARGET
{
return t_reflectcube.Sample(s_reflectcube, inp.texc);
}
#endif

View file

@ -25,10 +25,6 @@
float e_time; float e_time;
float3 e_eyepos; float3 e_eyepos;
float l_lightradius;
float3 l_lightcolour;
float3 l_lightposition;
sampler s_diffuse; /*diffuse*/ sampler s_diffuse; /*diffuse*/
sampler s_fullbright; /*normal*/ sampler s_fullbright; /*normal*/
float4 main (v2f inp) : COLOR0 float4 main (v2f inp) : COLOR0

View file

@ -0,0 +1,32 @@
struct a2v
{
float4 pos: POSITION;
};
struct v2f
{
#ifndef FRAGMENT_SHADER
float4 pos: POSITION;
#endif
float3 texc: TEXCOORD0;
};
#ifdef VERTEX_SHADER
float3 e_eyepos;
float4x4 m_modelviewprojection;
v2f main (a2v inp)
{
v2f outp;
outp.pos = mul(m_modelviewprojection, inp.pos);
outp.texc= inp.pos - e_eyepos;
outp.texc.y = -outp.texc;
return outp;
}
#endif
#ifdef FRAGMENT_SHADER
sampler s_reflectcube;
float4 main (v2f inp) : COLOR0
{
return texCUBE(s_reflectcube, inp.texc);
}
#endif

View file

@ -0,0 +1,30 @@
!!argb PREMUL=false
!!samps 1
#include "sys/defs.h"
//this shader is present for support for gles/gl3core contexts
//it is single-texture-with-vertex-colours, and doesn't do anything special.
//beware that a few things use this, including apparently fonts and bloom rescaling.
//its really not meant to do anything special.
layout(location=0) varying vec2 tc;
layout(location=1) varying vec4 vc;
#ifdef VERTEX_SHADER
void main ()
{
tc = v_texcoord;
vc = v_colour;
gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
void main ()
{
vec4 f = vc;
if (arg_PREMUL)
f.rgb *= f.a;
f *= texture2D(s_t0, tc);
gl_FragColor = f;
}
#endif

View file

@ -4,11 +4,12 @@
//!!permu SKELETAL //!!permu SKELETAL
!!permu FOG !!permu FOG
!!permu BUMP !!permu BUMP
!!permu REFLECTCUBEMASK
!!cvarf r_glsl_offsetmapping=0 !!cvarf r_glsl_offsetmapping=0
!!cvarf r_glsl_offsetmapping_scale=0.04 !!cvarf r_glsl_offsetmapping_scale=0.04
!!cvarf gl_specular=0 !!cvarf gl_specular=0
!!cvarb r_fog_exp2=true !!cvarb r_fog_exp2=true
!!samps diffuse normalmap upper lower specular fullbright !!samps diffuse normalmap upper lower specular fullbright reflectcube reflectmask
#include "sys/defs.h" #include "sys/defs.h"
@ -21,6 +22,7 @@ layout(location=1) varying vec3 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) #if defined(SPECULAR) || defined(OFFSETMAPPING)
layout(location=2) varying vec3 eyevector; layout(location=2) varying vec3 eyevector;
#endif #endif
layout(location=3) varying mat3 invsurface;
@ -30,7 +32,7 @@ layout(location=2) varying vec3 eyevector;
void main () void main ()
{ {
vec3 n; vec3 n;
if (SPECULAR||OFFSETMAPPING) if (SPECULAR||OFFSETMAPPING||REFLECTCUBEMASK)
{ {
vec3 s, t, w; vec3 s, t, w;
gl_Position = skeletaltransform_wnst(w,n,s,t); gl_Position = skeletaltransform_wnst(w,n,s,t);
@ -38,6 +40,13 @@ void main ()
eyevector.x = dot(eyeminusvertex, s.xyz); eyevector.x = dot(eyeminusvertex, s.xyz);
eyevector.y = dot(eyeminusvertex, t.xyz); eyevector.y = dot(eyeminusvertex, t.xyz);
eyevector.z = dot(eyeminusvertex, n.xyz); eyevector.z = dot(eyeminusvertex, n.xyz);
if (REFLECTCUBEMASK)
{
invsurface[0] = s;
invsurface[0] = t;
invsurface[0] = n;
}
} }
else else
{ {
@ -77,9 +86,10 @@ void main ()
col.rgb += lc.rgb*e_lowercolour*lc.a; col.rgb += lc.rgb*e_lowercolour*lc.a;
} }
vec3 bumps = vec3(0,0,1);
if (BUMP || SPECULAR) if (BUMP || SPECULAR)
{ {
vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5); bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);
vec4 specs = texture2D(s_specular, tc); vec4 specs = texture2D(s_specular, tc);
vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0)); vec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0));
@ -87,6 +97,14 @@ void main ()
col.rgb += cvar_gl_specular * spec * specs.rgb; col.rgb += cvar_gl_specular * spec * specs.rgb;
} }
if (REFLECTCUBEMASK)
{
vec3 rtc = reflect(-eyevector, bumps);
rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];
rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;
col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;
}
col.rgb *= light; col.rgb *= light;
if (FULLBRIGHT) if (FULLBRIGHT)

View file

@ -0,0 +1,24 @@
!!permu FOG
!!samps reflectcube
!!cvarb r_fog_exp2=true
#include "sys/defs.h"
#include "sys/fog.h"
//simple shader for simple skyboxes.
varying vec3 pos;
#ifdef VERTEX_SHADER
void main ()
{
pos = v_position.xyz - e_eyepos;
pos.y = -pos.y;
gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
void main ()
{
vec4 skybox = textureCube(s_reflectcube, pos);
gl_FragColor = vec4(fog3(skybox.rgb), 1.0);
}
#endif

View file

@ -10,6 +10,8 @@
!!argb vertexlit=0 !!argb vertexlit=0
!!samps paletted 1 !!samps paletted 1
!!argb eightbit=0 !!argb eightbit=0
!!argf mask=1.0
!!argb masklt=false
!!permu FOG !!permu FOG
//!!permu DELUXE //!!permu DELUXE
//!!permu LIGHTSTYLED //this seems to be breaking nvidia drivers if set from the engine, despite us not using it... //!!permu LIGHTSTYLED //this seems to be breaking nvidia drivers if set from the engine, despite us not using it...
@ -186,6 +188,20 @@ void main ()
//entity modifiers //entity modifiers
gl_FragColor = gl_FragColor * e_colourident; gl_FragColor = gl_FragColor * e_colourident;
if (arg_mask != 1.0)
{
if (arg_masklt)
{
if (gl_FragColor.a < arg_mask)
discard;
}
else
{
if (gl_FragColor.a >= arg_mask)
discard;
}
}
//and finally hide it all if we're fogged. //and finally hide it all if we're fogged.
#ifdef FOG #ifdef FOG
gl_FragColor = fog4(gl_FragColor); gl_FragColor = fog4(gl_FragColor);

View file

@ -160,7 +160,7 @@ typedef struct
vec3_t l_lightcolourscale; float l_lightradius; vec3_t l_lightcolourscale; float l_lightradius;
vec4_t l_shadowmapproj; vec4_t l_shadowmapproj;
vec2_t l_shadowmapscale; vec2_t pad3; vec2_t l_shadowmapscale; vec2_t pad3;
} cbuf_light_t; } vkcbuf_light_t;
//entity-specific constant-buffer //entity-specific constant-buffer
typedef struct typedef struct
@ -180,7 +180,7 @@ typedef struct
vec4_t e_colourident; vec4_t e_colourident;
vec4_t w_fogcolours; vec4_t w_fogcolours;
float w_fogdensity; float w_fogdepthbias; vec2_t pad7; float w_fogdensity; float w_fogdepthbias; vec2_t pad7;
} cbuf_entity_t; } vkcbuf_entity_t;
enum enum
{ {
@ -3094,6 +3094,8 @@ static qboolean BE_SetupMeshProgram(program_t *p, shaderpass_t *pass, unsigned i
perm |= PERMUTATION_FULLBRIGHT; perm |= PERMUTATION_FULLBRIGHT;
if (TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay)) if (TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay))
perm |= PERMUTATION_UPPERLOWER; perm |= PERMUTATION_UPPERLOWER;
if (TEXLOADED(shaderstate.curtexnums->reflectcube) || TEXLOADED(shaderstate.curtexnums->reflectmask))
perm |= PERMUTATION_REFLECTCUBEMASK;
if (r_refdef.globalfog.density) if (r_refdef.globalfog.density)
perm |= PERMUTATION_FOG; perm |= PERMUTATION_FOG;
// if (r_glsl_offsetmapping.ival && TEXLOADED(shaderstate.curtexnums->bump)) // if (r_glsl_offsetmapping.ival && TEXLOADED(shaderstate.curtexnums->bump))
@ -4166,7 +4168,7 @@ batch_t *VKBE_GetTempBatch(void)
void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour) void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour)
{ {
extern cvar_t gl_specular; extern cvar_t gl_specular;
cbuf_light_t *cbl = VKBE_AllocateBufferSpace(DB_UBO, (sizeof(*cbl) + 0x0ff) & ~0xff, &shaderstate.ubo_light.buffer, &shaderstate.ubo_light.offset); vkcbuf_light_t *cbl = VKBE_AllocateBufferSpace(DB_UBO, (sizeof(*cbl) + 0x0ff) & ~0xff, &shaderstate.ubo_light.buffer, &shaderstate.ubo_light.offset);
shaderstate.ubo_light.range = sizeof(*cbl); shaderstate.ubo_light.range = sizeof(*cbl);
if (!l) if (!l)
@ -4216,7 +4218,7 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod)
float ndr; float ndr;
float modelmatrix[16]; float modelmatrix[16];
float *m = modelmatrix; float *m = modelmatrix;
cbuf_entity_t *cbe = VKBE_AllocateBufferSpace(DB_UBO, (sizeof(*cbe) + 0x0ff) & ~0xff, &shaderstate.ubo_entity.buffer, &shaderstate.ubo_entity.offset); vkcbuf_entity_t *cbe = VKBE_AllocateBufferSpace(DB_UBO, (sizeof(*cbe) + 0x0ff) & ~0xff, &shaderstate.ubo_entity.buffer, &shaderstate.ubo_entity.offset);
shaderstate.ubo_entity.range = sizeof(*cbe); shaderstate.ubo_entity.range = sizeof(*cbe);
shaderstate.curentity = e; shaderstate.curentity = e;
@ -5163,12 +5165,13 @@ static void BE_SubmitMeshesSortList(batch_t *sortlist)
if (batch->shader->flags & SHADER_SKY) if (batch->shader->flags & SHADER_SKY)
{ {
if (!batch->shader->prog) if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)
{ {
if (shaderstate.mode == BEM_STANDARD) if (R_DrawSkyChain (batch))
R_DrawSkyChain (batch); continue;
continue;
} }
else if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)
continue;
} }
if ((batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME) if ((batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME)
@ -6195,7 +6198,7 @@ void VKBE_DrawWorld (batch_t **worldbatches)
shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
shaderstate.identitylighting = 1; shaderstate.identitylighting = r_lightmap_scale.value;
shaderstate.identitylighting *= r_refdef.hdr_value; shaderstate.identitylighting *= r_refdef.hdr_value;
shaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival); shaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);
@ -6214,18 +6217,19 @@ void VKBE_DrawWorld (batch_t **worldbatches)
VKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); VKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_OPAQUE);
#ifdef RTLIGHTS #ifdef RTLIGHTS
RSpeedRemark(); RSpeedRemark();
VKBE_SelectEntity(&r_worldentity); VKBE_SelectEntity(&r_worldentity);
Sh_DrawLights(r_refdef.scenevis); Sh_DrawLights(r_refdef.scenevis);
RSpeedEnd(RSPEED_STENCILSHADOWS); RSpeedEnd(RSPEED_RTLIGHTS);
#endif #endif
} }
RSpeedRemark();
VKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT); VKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_TRANSPARENTS);
if (r_wireframe.ival) if (r_wireframe.ival)
{ {
@ -6239,7 +6243,7 @@ void VKBE_DrawWorld (batch_t **worldbatches)
shaderstate.identitylighting = 1; shaderstate.identitylighting = 1;
shaderstate.identitylightmap = 1; shaderstate.identitylightmap = 1;
VKBE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT); VKBE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES); RSpeedEnd(RSPEED_TRANSPARENTS);
} }
R_RenderDlights (); R_RenderDlights ();

View file

@ -227,6 +227,7 @@ static void VK_DestroySwapChain(void)
VKBE_ShutdownFramePools(frame); VKBE_ShutdownFramePools(frame);
vkFreeCommandBuffers(vk.device, vk.cmdpool, frame->maxcbufs, frame->cbufs); vkFreeCommandBuffers(vk.device, vk.cmdpool, frame->maxcbufs, frame->cbufs);
BZ_Free(frame->cbufs);
vkDestroyFence(vk.device, frame->finishedfence, vkallocationcb); vkDestroyFence(vk.device, frame->finishedfence, vkallocationcb);
Z_Free(frame); Z_Free(frame);
} }
@ -3520,10 +3521,11 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
if (j == queue_count) if (j == queue_count)
{ {
//no queues can present to that surface, so I guess we can't use that device //no queues can present to that surface, so I guess we can't use that device
Con_DPrintf("vulkan: ignoring device %s as it can't present to window\n", props.deviceName); Con_DPrintf("vulkan: ignoring device \"%s\" as it can't present to window\n", props.deviceName);
continue; continue;
} }
} }
Con_DPrintf("Found Vulkan Device \"%s\"\n", props.deviceName);
if (!vk.gpu) if (!vk.gpu)
vk.gpu = devs[i]; vk.gpu = devs[i];
@ -3848,6 +3850,7 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
sh_config.env_add = false; //fixme: figure out what this means... sh_config.env_add = false; //fixme: figure out what this means...
sh_config.can_mipcap = true; sh_config.can_mipcap = true;
sh_config.havecubemaps = true;
VK_CheckTextureFormats(); VK_CheckTextureFormats();