1
0
Fork 0
forked from fte/fteqw

code to use occlusion queries for coronas.

tweaked cl_maxfps a little. now sticks much closer to the desired rate. also tweaked cl_netfps. clamps 77fps to 13ms frames (read: 76.9 fps) in an attempt to avoid tripping up any framerate checks (we had previously been using a lower net rate with occasional 14ms frames).
viewspace particles are now a thing.
greater control over spawning particles. its now possible to spawn particles only in slime, etc.
fix soundlength builtin.
preliminary version of r_dynamic -1, which can give some significant framerate boosts on certain maps.
fix halflife bsp texture issues.
rewrote worker thread logic. workers now work as a single pool, instead of independent pools. this means that you don't have to wait for a specific thread to finish before it can load new stuff, reducing overall load times some more. worker_count cvar allows reconfiguring the number of active workers. can be changed any time.
updated mod_terrain_create command. should be more useful now and make more sense when first loaded.
fix lit support. apparently its been broken for a while.
brush editor now has csg subtraction.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4990 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-10-11 11:34:58 +00:00
parent cc875358fd
commit 5172823341
76 changed files with 2964 additions and 877 deletions

View file

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h" #include "quakedef.h"
#include "particles.h" #include "particles.h"
#include "shader.h" #include "shader.h"
#include "glquake.h"
extern cvar_t cl_predict_players; extern cvar_t cl_predict_players;
extern cvar_t cl_predict_players_frac; extern cvar_t cl_predict_players_frac;
@ -100,8 +101,13 @@ void CL_FreeDlights(void)
int i; int i;
if (cl_dlights) if (cl_dlights)
for (i = 0; i < rtlights_max; i++) for (i = 0; i < rtlights_max; i++)
{
if (cl_dlights[i].worldshadowmesh) if (cl_dlights[i].worldshadowmesh)
SH_FreeShadowMesh(cl_dlights[i].worldshadowmesh); SH_FreeShadowMesh(cl_dlights[i].worldshadowmesh);
if (cl_dlights[i].coronaocclusionquery)
qglDeleteQueriesARB(1, &cl_dlights[i].coronaocclusionquery);
}
#endif #endif
rtlights_max = cl_maxdlights = 0; rtlights_max = cl_maxdlights = 0;
@ -110,6 +116,7 @@ void CL_FreeDlights(void)
} }
void CL_InitDlights(void) void CL_InitDlights(void)
{ {
CL_FreeDlights();
rtlights_max = cl_maxdlights = RTL_FIRST; rtlights_max = cl_maxdlights = RTL_FIRST;
cl_dlights = BZ_Realloc(cl_dlights, sizeof(*cl_dlights)*cl_maxdlights); cl_dlights = BZ_Realloc(cl_dlights, sizeof(*cl_dlights)*cl_maxdlights);
memset(cl_dlights, 0, sizeof(*cl_dlights)*cl_maxdlights); memset(cl_dlights, 0, sizeof(*cl_dlights)*cl_maxdlights);
@ -117,9 +124,12 @@ void CL_InitDlights(void)
static void CL_ClearDlight(dlight_t *dl, int key) static void CL_ClearDlight(dlight_t *dl, int key)
{ {
void *sm; void *sm = dl->worldshadowmesh;
sm = dl->worldshadowmesh; unsigned int oq = dl->coronaocclusionquery;
unsigned int oqr = (dl->key == key)?dl->coronaocclusionresult:false;
memset (dl, 0, sizeof(*dl)); memset (dl, 0, sizeof(*dl));
dl->coronaocclusionquery = oq;
dl->coronaocclusionresult = oqr;
dl->rebuildcache = true; dl->rebuildcache = true;
dl->worldshadowmesh = sm; dl->worldshadowmesh = sm;
dl->axis[0][0] = 1; dl->axis[0][0] = 1;
@ -222,12 +232,6 @@ dlight_t *CL_NewDlight (int key, const vec3_t org, float radius, float time,
return dl; return dl;
} }
dlight_t *CL_NewDlightRGB(int key, const vec3_t org, float radius, float time,
float r, float g, float b)
{
return CL_NewDlight(key, org, radius, time, r*5, g*5, b*5);
}
/* /*
=============== ===============
@ -1911,7 +1915,7 @@ void VQ2_AddLerpEntity(entity_t *in) //a convienience function
*/ */
int V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b) int V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b)
{ {
return CL_NewDlightRGB (entsource, org, quant, -0.1, r, g, b) - cl_dlights; return CL_NewDlight (entsource, org, quant, -0.1, r*5, g*5, b*5) - cl_dlights;
} }
void CLQ1_AddOrientedHalfSphere(shader_t *shader, float radius, float gap, float *matrix, float r, float g, float b, float a) void CLQ1_AddOrientedHalfSphere(shader_t *shader, float radius, float gap, float *matrix, float r, float g, float b, float a)
@ -2290,7 +2294,7 @@ void CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qbo
{ {
// int oldents = cl_numvisedicts; // int oldents = cl_numvisedicts;
// cl_numvisedicts = 0; // cl_numvisedicts = 0;
BE_DrawWorld(false, NULL); BE_DrawWorld(NULL, NULL);
cl_numstris = 0; cl_numstris = 0;
// cl_numvisedicts = oldents; // cl_numvisedicts = oldents;
} }
@ -2938,7 +2942,7 @@ void CL_LinkStaticEntities(void *pvs)
model_t *clmodel; model_t *clmodel;
extern cvar_t r_drawflame, gl_part_flame; extern cvar_t r_drawflame, gl_part_flame;
if (r_drawflame.ival < 0) if (r_drawflame.ival < 0 || r_drawentities.ival == 0)
return; return;
if (!cl.worldmodel) if (!cl.worldmodel)

View file

@ -31,6 +31,7 @@ static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue);
cvar_t cl_nodelta = CVAR("cl_nodelta","0"); cvar_t cl_nodelta = CVAR("cl_nodelta","0");
cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0");
cvar_t cl_c2spps = CVAR("cl_c2spps", "0"); cvar_t cl_c2spps = CVAR("cl_c2spps", "0");
cvar_t cl_c2sImpulseBackup = SCVAR("cl_c2sImpulseBackup","3"); cvar_t cl_c2sImpulseBackup = SCVAR("cl_c2sImpulseBackup","3");
cvar_t cl_netfps = CVAR("cl_netfps", "150"); cvar_t cl_netfps = CVAR("cl_netfps", "150");
@ -1177,10 +1178,16 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now re
fps = bound (6.7, wantfps, fpscap); //we actually cap ourselves to 150msecs (1000/7 = 142) fps = bound (6.7, wantfps, fpscap); //we actually cap ourselves to 150msecs (1000/7 = 142)
} }
//its not time yet
if (time < ceil(1000 / fps)) if (time < ceil(1000 / fps))
return 0; return 0;
return time - ceil(1000 / fps); //clamp it if we have over 1.5 frame banked somehow
if (time - (1000 / fps) > (1000 / fps)*1.5)
return (1000 / fps) * 1.5;
//report how much spare time the caller now has
return time - (1000 / fps);
} }
qboolean allowindepphys; qboolean allowindepphys;
@ -1320,10 +1327,6 @@ int CL_IndepPhysicsThread(void *param)
spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, false); spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, false);
if (spare) if (spare)
{ {
//don't let them bank too much and get sudden bursts
if (spare > 15)
spare = 15;
time -= spare/1000.0f; time -= spare/1000.0f;
Sys_LockMutex(indeplock); Sys_LockMutex(indeplock);
if (cls.state) if (cls.state)
@ -1529,6 +1532,7 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)
cmd->lightlevel = 0; cmd->lightlevel = 0;
#ifdef CSQC_DAT #ifdef CSQC_DAT
if (!runningindepphys)
CSQC_Input_Frame(plnum, cmd); CSQC_Input_Frame(plnum, cmd);
#endif #endif
memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum])); memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum]));
@ -1631,8 +1635,10 @@ void CL_SendCmd (double frametime, qboolean mainloop)
static float pps_balance = 0; static float pps_balance = 0;
static int dropcount = 0; static int dropcount = 0;
static double msecs; static double msecs;
static double msecsround;
int msecstouse; int msecstouse;
qboolean dontdrop=false; qboolean dontdrop=false;
float usetime;
clcmdbuf_t *next; clcmdbuf_t *next;
@ -1739,39 +1745,28 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#ifdef IRCCONNECT #ifdef IRCCONNECT
if (cls.netchan.remote_address.type != NA_IRC) if (cls.netchan.remote_address.type != NA_IRC)
#endif #endif
if (msecs>150) //q2 has 200 slop.
msecs=150;
msecs += frametime*1000; msecs += frametime*1000;
// Con_Printf("%f\n", msecs); // Con_Printf("%f\n", msecs);
if (msecs<0)
msecs=0; //erm.
msecstouse = (int)msecs; //casts round down.
if (msecstouse == 0)
return;
#ifdef IRCCONNECT #ifdef IRCCONNECT
if (cls.netchan.remote_address.type != NA_IRC) if (cls.netchan.remote_address.type != NA_IRC)
#endif #endif
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
wantfps = cl_netfps.value; wantfps = cl_netfps.value;
fullsend = true; fullsend = true;
msecstouse = 0;
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (sv.state && cls.state != ca_active) if (sv.state && cls.state != ca_active)
{
fullsend = -1; fullsend = -1;
msecstouse = usetime = msecs;
msecs = 0;
}
else else
#endif #endif
if (!runningindepphys)
{ {
// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up // while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up
if (cls.state < ca_active && !cls.download) if (cls.state < ca_active && !cls.download)
@ -1783,22 +1778,41 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#endif #endif
wantfps = 12.5; wantfps = 12.5;
} }
if (cl_netfps.value > 0 || !fullsend) if (!runningindepphys && (cl_netfps.value > 0 || !fullsend))
{ {
float spare; float spare;
spare = CL_FilterTime(msecstouse, wantfps, false); spare = CL_FilterTime(msecs, wantfps, false);
if (!spare && (msecstouse < 200 usetime = msecsround + (msecs - spare);
#ifdef IRCCONNECT msecstouse = (int)usetime;
|| cls.netchan.remote_address.type == NA_IRC if (!spare)
#endif
))
fullsend = false; fullsend = false;
if (spare > cl_sparemsec.ival) else
spare = cl_sparemsec.ival; {
if (spare > 0) msecsround = usetime - msecstouse;
msecstouse -= spare; 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.
#ifdef HLCLIENT #ifdef HLCLIENT
if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0])) if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0]))
@ -1829,7 +1843,12 @@ void CL_SendCmd (double frametime, qboolean mainloop)
// if (cl.spectator) // if (cl.spectator)
Cam_Track(&cl.playerview[plnum], &independantphysics[plnum]); Cam_Track(&cl.playerview[plnum], &independantphysics[plnum]);
Cam_FinishMove(&cl.playerview[plnum], &independantphysics[plnum]); Cam_FinishMove(&cl.playerview[plnum], &independantphysics[plnum]);
independantphysics[plnum].msec = msecstouse; 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)
//so lets just never use 12.
if (fullsend && (independantphysics[plnum].msec > 12.9 && independantphysics[plnum].msec < 13) && cls.maxfps == 77)
independantphysics[plnum].msec = 13;
} }
//the main loop isn't allowed to send //the main loop isn't allowed to send
@ -1839,9 +1858,12 @@ void CL_SendCmd (double frametime, qboolean mainloop)
// if (skipcmd) // if (skipcmd)
// return; // return;
if (!fullsend || !msecstouse) if (!fullsend)
return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems
if (msecstouse == 12)
msecstouse = 13;
// if (msecstouse > 127) // if (msecstouse > 127)
// Con_Printf("%i\n", msecstouse, msecs); // Con_Printf("%i\n", msecstouse, msecs);
@ -2003,6 +2025,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
// //
// deliver the message // deliver the message
// //
cls.netchan.dupe = cl_c2sdupe.ival;
Netchan_Transmit (&cls.netchan, buf.cursize, buf.data, 2500); Netchan_Transmit (&cls.netchan, buf.cursize, buf.data, 2500);
if (cls.netchan.fatal_error) if (cls.netchan.fatal_error)
@ -2051,6 +2074,7 @@ void CL_InitInput (void)
Cvar_Register (&prox_inmenu, inputnetworkcvargroup); Cvar_Register (&prox_inmenu, inputnetworkcvargroup);
Cvar_Register (&cl_c2sdupe, inputnetworkcvargroup);
Cvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup); Cvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup);
Cvar_Register (&cl_c2spps, inputnetworkcvargroup); Cvar_Register (&cl_c2spps, inputnetworkcvargroup);
Cvar_Register (&cl_queueimpulses, inputnetworkcvargroup); Cvar_Register (&cl_queueimpulses, inputnetworkcvargroup);

View file

@ -4861,8 +4861,8 @@ double Host_Frame (double time)
) )
{ {
// realtime += spare/1000; //don't use it all! // realtime += spare/1000; //don't use it all!
spare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver); double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver);
if (!spare) if (!newspare)
{ {
while(COM_DoWork(0, false)) while(COM_DoWork(0, false))
; ;
@ -4872,6 +4872,7 @@ double Host_Frame (double time)
spare = 0; //uncapped. spare = 0; //uncapped.
if (spare > cl_sparemsec.ival) if (spare > cl_sparemsec.ival)
spare = cl_sparemsec.ival; spare = cl_sparemsec.ival;
spare = newspare;
// realtime -= spare/1000; //don't use it all! // realtime -= spare/1000; //don't use it all!
} }
@ -4889,7 +4890,7 @@ double Host_Frame (double time)
CL_ProgressDemoTime(); CL_ProgressDemoTime();
hadwork = haswork; hadwork = haswork;
} }
cl.stillloading = cl.sendprespawn || (cls.state < ca_active && COM_HasWork()); cl.stillloading = cl.sendprespawn || (cls.state < ca_active && worker_flush.ival && COM_HasWork());
COM_MainThreadWork(); COM_MainThreadWork();

View file

@ -5835,7 +5835,7 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n
{ {
flocation_t loc; flocation_t loc;
Cmd_TokenizeString(stufftext+2, false, false); Cmd_TokenizeString(stufftext+2, false, false);
if (FS_FLocateFile(Cmd_Argv(1), FSLFRT_IFFOUND, &loc)) if (FS_FLocateFile(Cmd_Argv(1), FSLF_IFFOUND, &loc))
Con_Printf("You have been kicked due to the file \"%s\" being modified.\n", Cmd_Argv(1)); Con_Printf("You have been kicked due to the file \"%s\" being modified.\n", Cmd_Argv(1));
} }
#ifdef PLUGINS #ifdef PLUGINS
@ -6568,13 +6568,13 @@ void CLQW_ParseServerMessage (void)
case svcfte_cgamepacket: case svcfte_cgamepacket:
csqcpacket = true; csqcpacket = true;
#ifdef CSQC_DAT
if (CSQC_ParseGamePacket())
break;
#endif
#ifdef HLCLIENT #ifdef HLCLIENT
if (CLHL_ParseGamePacket()) if (CLHL_ParseGamePacket())
break; break;
#endif
#ifdef CSQC_DAT
if (CSQC_ParseGamePacket())
break;
#endif #endif
Con_Printf("Unable to parse gamecode packet\n"); Con_Printf("Unable to parse gamecode packet\n");
break; break;

View file

@ -1246,7 +1246,6 @@ void CL_PredictMovePNum (int seat)
{ {
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
{ {
extern cvar_t temp1;
pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i]; pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i];
pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i]; pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i];

View file

@ -1096,6 +1096,7 @@ void CL_ParseTEnt (void)
if (nqprot) if (nqprot)
{ {
//easiest way to handle these //easiest way to handle these
//should probably also do qwgunshot ones with nq protocols or something
switch(type) switch(type)
{ {
case TENQ_EXPLOSION2: case TENQ_EXPLOSION2:
@ -1107,10 +1108,41 @@ void CL_ParseTEnt (void)
case TE_EXPLOSION: case TE_EXPLOSION:
type = TEQW_EXPLOSIONNOSPRITE; type = TEQW_EXPLOSIONNOSPRITE;
break; break;
case TE_GUNSHOT:
type = TE_GUNSHOT_NQCOMPAT;
break;
case TE_GUNSHOT_NQCOMPAT:
type = TE_GUNSHOT;
break;
default: default:
break; break;
} }
} }
//right, nq vs qw doesn't matter now, supposedly.
if (cl_shownet.ival >= 2)
{
static char *te_names[] = {
"spike", "superspike", "qwgunshot", "qwexplosion",
"tarexplosion", "lightning1", "lightning2", "wizspike",
"knightspike", "lightning3", "lavasplash", "teleport",
"blood", "lightningblood", "bullet", "superbullet", //bullets deprecated
"railtrail", "beam", "explosion2", "nqexplosion",
"nqgunshot", "?", "?", "?",
#ifdef HEXEN2
"h2lightsml", "h2chain", "h2sunstf1", "h2sunstf2",
"h2light", "h2cb", "h2ic", "h2gaze",
"h2famine", "h2partexp"
#endif
};
if (type < countof(te_names))
Con_Printf(" te_%s\n", te_names[type]);
else
Con_Printf(" te_%i\n", type);
}
switch (type) switch (type)
{ {
case TE_WIZSPIKE: // spike hitting wall case TE_WIZSPIKE: // spike hitting wall
@ -1509,8 +1541,10 @@ void CL_ParseTEnt (void)
P_RunParticleEffect (pos, vec3_origin, 0, 20); P_RunParticleEffect (pos, vec3_origin, 0, 20);
break; break;
case TE_GUNSHOT: // bullet hitting wall case TE_GUNSHOT: // bullet hitting wall
if (nqprot) case TE_GUNSHOT_NQCOMPAT:
if (type == TE_GUNSHOT_NQCOMPAT)
cnt = 1; cnt = 1;
else else
cnt = MSG_ReadByte (); cnt = MSG_ReadByte ();
@ -3301,7 +3335,7 @@ fixme:
ex = CL_AllocExplosion (pos); ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
ex->start = cl.q2frame.servertime - 100; ex->start = cl.q2frame.servertime - 100;
CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0.1, 0); CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5);
P_RunParticleEffectTypeString(pos, NULL, 1, "te_muzzleflash"); P_RunParticleEffectTypeString(pos, NULL, 1, "te_muzzleflash");
break; break;
case CRTE_BLUE_MUZZLEFLASH: case CRTE_BLUE_MUZZLEFLASH:
@ -3309,7 +3343,7 @@ fixme:
ex = CL_AllocExplosion (pos); ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
ex->start = cl.q2frame.servertime - 100; ex->start = cl.q2frame.servertime - 100;
CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0.1, 0); CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5);
P_RunParticleEffectTypeString(pos, NULL, 1, "te_blue_muzzleflash"); P_RunParticleEffectTypeString(pos, NULL, 1, "te_blue_muzzleflash");
break; break;
case CRTE_SMART_MUZZLEFLASH: case CRTE_SMART_MUZZLEFLASH:
@ -3317,7 +3351,7 @@ fixme:
ex = CL_AllocExplosion (pos); ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
ex->start = cl.q2frame.servertime - 100; ex->start = cl.q2frame.servertime - 100;
CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0, 0.2); CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5);
P_RunParticleEffectTypeString(pos, NULL, 1, "te_smart_muzzleflash"); P_RunParticleEffectTypeString(pos, NULL, 1, "te_smart_muzzleflash");
break; break;
case CRTE_LEADERFIELD: case CRTE_LEADERFIELD:
@ -3328,7 +3362,7 @@ fixme:
VectorCopy (pos, ex->origin); VectorCopy (pos, ex->origin);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
ex->start = cl.q2frame.servertime - 100; ex->start = cl.q2frame.servertime - 100;
CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0, 0.2); CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5);
P_RunParticleEffectTypeString(pos, NULL, 1, "te_deathfield"); P_RunParticleEffectTypeString(pos, NULL, 1, "te_deathfield");
break; break;
case CRTE_BLASTERBEAM: case CRTE_BLASTERBEAM:

View file

@ -309,8 +309,8 @@ typedef struct
void (QDECL *player_setkey) (char *key, char *value); //wait, no pnum? void (QDECL *player_setkey) (char *key, char *value); //wait, no pnum?
qboolean (QDECL *getcdkey) (int playernum, char key[16]); qboolean (QDECL *getcdkey) (int playernum, char key[16]);
int trackerfromplayer; int (QDECL *trackerfromplayer) (int pl);
int playerfromtracker; int (QDECL *playerfromtracker) (int tr);
int (QDECL *sendcmd_unreliable) (char *cmd); int (QDECL *sendcmd_unreliable) (char *cmd);
void (QDECL *getsysmousepos) (long *xandy); void (QDECL *getsysmousepos) (long *xandy);
void (QDECL *setsysmousepos) (int x, int y); void (QDECL *setsysmousepos) (int x, int y);
@ -707,8 +707,8 @@ void QDECL CLGHL_getplayerinfo (int entnum, hlplayerinfo_t *result)
result->isus = true; result->isus = true;
result->isspec = player->spectator; result->isspec = player->spectator;
result->pl = player->pl; result->pl = player->pl;
if (player->skin) if (player->qwskin)
result->model = player->skin->name; result->model = player->qwskin->name;
else else
result->model = ""; result->model = "";
} }
@ -722,7 +722,7 @@ void QDECL CLGHL_startsound_name (char *name, float vol)
Con_Printf ("CLGHL_startsound_name: can't cache %s\n", name); Con_Printf ("CLGHL_startsound_name: can't cache %s\n", name);
return; return;
} }
S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0); S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0);
} }
void QDECL CLGHL_startsound_idx (int idx, float vol) void QDECL CLGHL_startsound_idx (int idx, float vol)
{ {
@ -733,7 +733,7 @@ void QDECL CLGHL_startsound_idx (int idx, float vol)
Con_Printf ("CLGHL_startsound_name: index not precached %s\n", name); Con_Printf ("CLGHL_startsound_name: index not precached %s\n", name);
return; return;
} }
S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0); S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0);
} }
void QDECL CLGHL_anglevectors (float *ina, float *outf, float *outr, float *outu) void QDECL CLGHL_anglevectors (float *ina, float *outf, float *outr, float *outu)
@ -1175,8 +1175,8 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =
CLGHL_player_setkey, CLGHL_player_setkey,
CLGHL_getcdkey, CLGHL_getcdkey,
(void*)0xdeaddead,//CLGHL_trackerfromplayer; CLGHL_trackerfromplayer,
(void*)0xdeaddead,//CLGHL_playerfromtracker; CLGHL_playerfromtracker,
CLGHL_sendcmd_unreliable, CLGHL_sendcmd_unreliable,
CLGHL_getsysmousepos, CLGHL_getsysmousepos,
CLGHL_setsysmousepos, CLGHL_setsysmousepos,
@ -1186,7 +1186,7 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =
0xdeadbeef 0xdeadbeef
}; };
dllhandle_t clg; dllhandle_t *clg;
int CLHL_GamecodeDoesMouse(void) int CLHL_GamecodeDoesMouse(void)
{ {
@ -1270,7 +1270,8 @@ void CLHL_LoadClientGame(void)
clg = NULL; clg = NULL;
iterator = NULL; iterator = NULL;
while(COM_IteratePaths(&iterator, path, sizeof(path))) //FIXME: dlls in gamepaths is evil
while(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0))
{ {
snprintf (fullname, sizeof(fullname), "%s%s", path, "cl_dlls/client"); snprintf (fullname, sizeof(fullname), "%s%s", path, "cl_dlls/client");
clg = Sys_LoadLibrary(fullname, funcs); clg = Sys_LoadLibrary(fullname, funcs);
@ -1308,6 +1309,20 @@ void CLHL_LoadClientGame(void)
CLHL_cgamefuncs.HUD_Init(); CLHL_cgamefuncs.HUD_Init();
if (CLHL_cgamefuncs.HUD_VidInit) if (CLHL_cgamefuncs.HUD_VidInit)
CLHL_cgamefuncs.HUD_VidInit(); CLHL_cgamefuncs.HUD_VidInit();
{
struct hlkbutton_s
{
int down[2]; // key nums holding it down
int state; // low bit is down state
} *but;
struct hlkbutton_s *(QDECL *pKB_Find) (const char *foo) = (void*)Sys_GetAddressForName(clg, "KB_Find");
if (pKB_Find)
{
but = pKB_Find("in_mlook");
if (but)
but->state |= 1;
}
}
} }
int CLHL_BuildUserInput(int msecs, usercmd_t *cmd) int CLHL_BuildUserInput(int msecs, usercmd_t *cmd)
@ -1329,7 +1344,7 @@ int CLHL_BuildUserInput(int msecs, usercmd_t *cmd)
cmd->upmove = hlcmd.upmove; cmd->upmove = hlcmd.upmove;
cmd->weapon = hlcmd.weaponselect; cmd->weapon = hlcmd.weaponselect;
cmd->impulse = hlcmd.impulse; cmd->impulse = hlcmd.impulse;
cmd->buttons = hlcmd.buttons; cmd->buttons = hlcmd.buttons & 0xff; //FIXME: quake's protocols are more limited than this
cmd->lightlevel = hlcmd.lightlevel; cmd->lightlevel = hlcmd.lightlevel;
return true; return true;
#else #else
@ -1365,7 +1380,7 @@ int CLHL_DrawHud(void)
CLHL_cgamefuncs.HUD_UpdateClientData(&state, cl.time); CLHL_cgamefuncs.HUD_UpdateClientData(&state, cl.time);
ret = CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermission); ret = CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermissionmode != IM_NONE);
return ret; return ret;
} }
@ -1383,7 +1398,7 @@ int CLHL_AnimateViewEntity(entity_t *ent)
return true; return true;
} }
explosion_t *CL_AllocExplosion (void); explosion_t *CL_AllocExplosion (vec3_t org);
int CLHL_ParseGamePacket(void) int CLHL_ParseGamePacket(void)
{ {
@ -1445,19 +1460,18 @@ int CLHL_ParseGamePacket(void)
if (!(flags & 8)) if (!(flags & 8))
P_RunParticleEffectType(startp, NULL, 1, pt_explosion); P_RunParticleEffectType(startp, NULL, 1, pt_explosion);
if (!(flags & 4)) if (!(flags & 4))
S_StartSound(0, 0, S_PrecacheSound("explosion"), startp, 1, 1, 0, 0); S_StartSound(0, 0, S_PrecacheSound("explosion"), startp, 1, 1, 0, 0, 0);
if (!(flags & 2)) if (!(flags & 2))
CL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0); CL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0);
ef = CL_AllocExplosion(); ef = CL_AllocExplosion(startp);
VectorCopy(startp, ef->origin);
ef->start = cl.time; ef->start = cl.time;
ef->model = cl.model_precache[midx]; ef->model = cl.model_precache[midx];
ef->framerate = mrate; ef->framerate = mrate;
ef->firstframe = 0; ef->firstframe = 0;
ef->numframes = ef->model->numframes; ef->numframes = ef->model->numframes;
if (!(flags & 1)) if (!(flags & 1))
ef->flags = Q2RF_ADDITIVE; ef->flags = RF_ADDITIVE;
else else
ef->flags = 0; ef->flags = 0;
break; break;
@ -1503,8 +1517,7 @@ int CLHL_ParseGamePacket(void)
MSG_ReadByte(); MSG_ReadByte();
lifetime = MSG_ReadByte(); lifetime = MSG_ReadByte();
ef = CL_AllocExplosion(); ef = CL_AllocExplosion(startp);
VectorCopy(startp, ef->origin);
ef->start = cl.time; ef->start = cl.time;
ef->angles[1] = ang; ef->angles[1] = ang;
ef->model = cl.model_precache[midx]; ef->model = cl.model_precache[midx];
@ -1554,7 +1567,7 @@ int CLHL_ParseGamePacket(void)
break; break;
case svc_intermission: case svc_intermission:
//nothing. //nothing.
cl.intermission = true; cl.intermissionmode = IM_NQSCORES;
break; break;
case svc_cdtrack: case svc_cdtrack:
{ {
@ -1572,7 +1585,7 @@ int CLHL_ParseGamePacket(void)
break; break;
case 37: //svc_roomtype case 37: //svc_roomtype
tempi = MSG_ReadShort(); tempi = MSG_ReadShort();
S_SetUnderWater(tempi==14||tempi==15||tempi==16); // S_SetUnderWater(tempi==14||tempi==15||tempi==16);
break; break;
default: default:
Con_Printf("Unrecognised gamecode packet %i (%s)\n", subcode, usermsgs[subcode].name); Con_Printf("Unrecognised gamecode packet %i (%s)\n", subcode, usermsgs[subcode].name);

View file

@ -303,6 +303,9 @@ typedef struct dlight_s
unsigned int flags; unsigned int flags;
char cubemapname[64]; char cubemapname[64];
int coronaocclusionquery;
unsigned int coronaocclusionresult;
//the following are used for rendering (client code should clear on create) //the following are used for rendering (client code should clear on create)
qboolean rebuildcache; qboolean rebuildcache;
struct shadowmesh_s *worldshadowmesh; struct shadowmesh_s *worldshadowmesh;
@ -975,7 +978,6 @@ void CL_FreeDlights(void);
dlight_t *CL_AllocDlight (int key); dlight_t *CL_AllocDlight (int key);
dlight_t *CL_AllocSlight (void); //allocates a static light dlight_t *CL_AllocSlight (void); //allocates a static light
dlight_t *CL_NewDlight (int key, const vec3_t origin, float radius, float time, float r, float g, float b); dlight_t *CL_NewDlight (int key, const vec3_t origin, float radius, float time, float r, float g, float b);
dlight_t *CL_NewDlightRGB (int key, const vec3_t origin, float radius, float time, float r, float g, float b);
dlight_t *CL_NewDlightCube (int key, const vec3_t origin, vec3_t angles, float radius, float time, vec3_t colours); dlight_t *CL_NewDlightCube (int key, const vec3_t origin, vec3_t angles, float radius, float time, vec3_t colours);
void CL_DecayLights (void); void CL_DecayLights (void);

View file

@ -971,6 +971,17 @@ void VARGS Con_SafeTPrintf (translation_t text, ...)
Con_Printf ("%s", msg); Con_Printf ("%s", msg);
} }
static void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b)
{
if (!developer.value)
Con_Log(data);
else
{
Sys_Printf ("%s", data); // also echo to debugging console
Con_PrintCon(&con_main, data, con_main.parseflags);
}
BZ_Free(data);
}
/* /*
================ ================
Con_DPrintf Con_DPrintf
@ -999,6 +1010,13 @@ void VARGS Con_DPrintf (const char *fmt, ...)
vsnprintf (msg,sizeof(msg)-1, fmt,argptr); vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
va_end (argptr); va_end (argptr);
if (!Sys_IsMainThread())
{
if (developer.ival)
COM_AddWork(0, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0);
return;
}
if (!developer.value) if (!developer.value)
Con_Log(msg); Con_Log(msg);
else else

View file

@ -28,7 +28,9 @@ char *r_defaultimageextensions =
#if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB) #if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB)
" jpg" //q3 uses some jpegs, for some reason " jpg" //q3 uses some jpegs, for some reason
#endif #endif
#ifndef NOLEGACY
" pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs. " pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs.
#endif
; ;
static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);
cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load."); cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load.");
@ -2783,7 +2785,9 @@ static struct
{3, "textures/%s/%s%s", 1}, /*fuhquake compatibility*/ {3, "textures/%s/%s%s", 1}, /*fuhquake compatibility*/
{3, "%s/%s%s", 1}, /*fuhquake compatibility*/ {3, "%s/%s%s", 1}, /*fuhquake compatibility*/
{2, "textures/%s%s", 1}, /*directly named texture with textures/ prefix*/ {2, "textures/%s%s", 1}, /*directly named texture with textures/ prefix*/
#ifndef NOLEGACY
{2, "override/%s%s", 1} /*tenebrae compatibility*/ {2, "override/%s%s", 1} /*tenebrae compatibility*/
#endif
}; };
static void Image_MipMap8888 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) static void Image_MipMap8888 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)
@ -3676,6 +3680,54 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
freedata = true; freedata = true;
break; break;
case TF_MIP4_8PAL24:
//8bit opaque data
{
unsigned int pixels =
(imgwidth>>0) * (imgheight>>0) +
(imgwidth>>1) * (imgheight>>1) +
(imgwidth>>2) * (imgheight>>2) +
(imgwidth>>3) * (imgheight>>3);
palettedata = (qbyte*)rawdata + pixels;
Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags);
flags |= IF_NOPICMIP;
if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && sh_config.texfmt[PTI_RGBX8])
{
unsigned int pixels =
(imgwidth>>0) * (imgheight>>0) +
(imgwidth>>1) * (imgheight>>1) +
(imgwidth>>2) * (imgheight>>2) +
(imgwidth>>3) * (imgheight>>3);
mips->encoding = PTI_RGBX8;
rgbadata = BZ_Malloc(pixels*4);
for (i = 0; i < pixels; i++)
{
qbyte *p = ((qbyte*)palettedata) + ((qbyte*)rawdata)[i]*3;
//FIXME: endian
rgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16);
}
for (i = 0; i < 4; i++)
{
mips->mip[i].width = imgwidth>>i;
mips->mip[i].height = imgheight>>i;
mips->mip[i].datasize = mips->mip[i].width * mips->mip[i].height * 4;
mips->mip[i].needfree = false;
}
mips->mipcount = i;
mips->mip[0].data = rgbadata;
mips->mip[1].data = (qbyte*)mips->mip[0].data + mips->mip[0].datasize;
mips->mip[2].data = (qbyte*)mips->mip[1].data + mips->mip[1].datasize;
mips->mip[3].data = (qbyte*)mips->mip[2].data + mips->mip[2].datasize;
mips->extrafree = rgbadata;
if (freedata)
BZ_Free(rawdata);
return true;
}
}
//fall through
case TF_8PAL24: case TF_8PAL24:
if (!palettedata) if (!palettedata)
{ {
@ -4458,6 +4510,8 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned
case TF_BGRA32: case TF_BGRA32:
b *= 4; b *= 4;
break; break;
case TF_MIP4_8PAL24:
pb = 3*256;
case TF_MIP4_LUM8: case TF_MIP4_LUM8:
case TF_MIP4_SOLID8: case TF_MIP4_SOLID8:
b = (fallbackwidth>>0)*(fallbackheight>>0) + b = (fallbackwidth>>0)*(fallbackheight>>0) +
@ -4508,9 +4562,9 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned
else else
#endif #endif
if (lowpri) if (lowpri)
COM_AddWork(5, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
else else
COM_AddWork(2+(seq++%3), Image_LoadHiResTextureWorker, tex, NULL, 0, 0); COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
} }
return tex; return tex;
} }
@ -4711,6 +4765,7 @@ void Image_List_f(void)
//may not create any images yet. //may not create any images yet.
void Image_Init(void) void Image_Init(void)
{ {
wadmutex = Sys_CreateMutex();
memset(imagetablebuckets, 0, sizeof(imagetablebuckets)); memset(imagetablebuckets, 0, sizeof(imagetablebuckets));
Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets); Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets);
@ -4736,6 +4791,10 @@ void Image_Shutdown(void)
} }
if (i) if (i)
Con_DPrintf("Destroyed %i/%i images\n", j, i); Con_DPrintf("Destroyed %i/%i images\n", j, i);
if (wadmutex)
Sys_DestroyMutex(wadmutex);
wadmutex = NULL;
} }
//load the named file, without failing. //load the named file, without failing.

View file

@ -433,6 +433,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum)
int mfwt; int mfwt;
qboolean strafe_x, strafe_y; qboolean strafe_x, strafe_y;
int wpnum; int wpnum;
extern qboolean runningindepphys;
//small performance boost //small performance boost
if (mouse->type == M_INVALID) if (mouse->type == M_INVALID)
@ -570,7 +571,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum)
#ifdef PEXT_CSQC #ifdef PEXT_CSQC
if (mouse->type == M_TOUCH) if (mouse->type == M_TOUCH)
{ {
if (CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid)) if (!runningindepphys && CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid))
{ {
mx = 0; mx = 0;
my = 0; my = 0;
@ -579,7 +580,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum)
else else
{ {
if (mx || my) if (mx || my)
if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) if (!runningindepphys && CSQC_MouseMove(mx, my, mouse->qdeviceid))
{ {
mx = 0; mx = 0;
my = 0; my = 0;

View file

@ -1651,7 +1651,7 @@ cin_t *Media_WinAvi_TryLoad(char *name)
return NULL; return NULL;
FS_FLocateFile(name, FSLFRT_DEPTH_OSONLY, &loc); FS_FLocateFile(name, FSLF_IFFOUND, &loc);
if (!loc.offset && *loc.rawname && !qAVIFileOpenA(&pavi, loc.rawname, OF_READ, NULL))//!AVIStreamOpenFromFile(&pavi, name, streamtypeVIDEO, 0, OF_READ, NULL)) if (!loc.offset && *loc.rawname && !qAVIFileOpenA(&pavi, loc.rawname, OF_READ, NULL))//!AVIStreamOpenFromFile(&pavi, name, streamtypeVIDEO, 0, OF_READ, NULL))
{ {
@ -4424,6 +4424,15 @@ static void S_MP3_Purge(sfx_t *sfx)
sfx->loadstate = SLS_NOTLOADED; sfx->loadstate = SLS_NOTLOADED;
} }
float S_MP3_Query(sfx_t *sfx, sfxcache_t *buf)
{
//we don't know unless we decode it all
if (buf)
{
}
return 0;
}
/*must be thread safe*/ /*must be thread safe*/
sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length) sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{ {
@ -4552,6 +4561,7 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
s->decoder.ended = S_MP3_Purge; s->decoder.ended = S_MP3_Purge;
s->decoder.purge = S_MP3_Purge; s->decoder.purge = S_MP3_Purge;
s->decoder.decodedata = S_MP3_Locate; s->decoder.decodedata = S_MP3_Locate;
s->decoder.querydata = S_MP3_Query;
dec->dstdata = NULL; dec->dstdata = NULL;
dec->dstcount = 0; dec->dstcount = 0;

View file

@ -190,6 +190,7 @@ typedef enum uploadfmt
TF_LUM8, /*8bit greyscale image*/ TF_LUM8, /*8bit greyscale image*/
TF_MIP4_LUM8, /*8bit 4-mip greyscale image*/ TF_MIP4_LUM8, /*8bit 4-mip greyscale image*/
TF_MIP4_SOLID8, /*8bit 4-mip image*/ TF_MIP4_SOLID8, /*8bit 4-mip image*/
TF_MIP4_8PAL24, /*8bit 4-mip image*/
TF_SOLID8, /*8bit quake-palette image*/ TF_SOLID8, /*8bit quake-palette image*/
TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/ TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/
TF_TRANS8_FULLBRIGHT, /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/ TF_TRANS8_FULLBRIGHT, /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/
@ -427,7 +428,7 @@ typedef struct rendererinfo_s {
void (*BE_SubmitBatch)(struct batch_s *batch); void (*BE_SubmitBatch)(struct batch_s *batch);
struct batch_s *(*BE_GetTempBatch)(void); struct batch_s *(*BE_GetTempBatch)(void);
//Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required //Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required
void (*BE_DrawWorld) (qboolean drawworld, qbyte *vis); void (*BE_DrawWorld) (struct batch_s **worldbatches, qbyte *vis);
//called at init, force the display to the right defaults etc //called at init, force the display to the right defaults etc
void (*BE_Init)(void); void (*BE_Init)(void);
//Generates an optimised VBO, one for each texture on the map //Generates an optimised VBO, one for each texture on the map

View file

@ -227,6 +227,7 @@ typedef struct part_type_s {
float orgadd, randomorgadd; //spawn the particle this far along its velocity direction float orgadd, randomorgadd; //spawn the particle this far along its velocity direction
float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards) float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards)
vec3_t orgbias; //static 3d world-coord bias vec3_t orgbias; //static 3d world-coord bias
float viewspacefrac;
float s1, t1, s2, t2; //texture coords float s1, t1, s2, t2; //texture coords
float texsstride; //addition for s for each random slot. float texsstride; //addition for s for each random slot.
@ -316,6 +317,7 @@ typedef struct part_type_s {
#define PT_TROVERWATER 0x0200 // don't spawn if underwater #define PT_TROVERWATER 0x0200 // don't spawn if underwater
#define PT_TRUNDERWATER 0x0400 // don't spawn if overwater #define PT_TRUNDERWATER 0x0400 // don't spawn if overwater
#define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows. #define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows.
unsigned int fluidmask;
unsigned int state; unsigned int state;
#define PS_INRUNLIST 0x1 // particle type is currently in execution list #define PS_INRUNLIST 0x1 // particle type is currently in execution list
@ -953,6 +955,7 @@ static void P_ResetToDefaults(part_type_t *ptype)
ptype->inwater = P_INVALID; ptype->inwater = P_INVALID;
ptype->cliptype = P_INVALID; ptype->cliptype = P_INVALID;
ptype->emit = P_INVALID; ptype->emit = P_INVALID;
ptype->fluidmask = FTECONTENTS_FLUID;
ptype->alpha = 1; ptype->alpha = 1;
ptype->alphachange = 1; ptype->alphachange = 1;
ptype->clipbounce = 0.8; ptype->clipbounce = 0.8;
@ -1243,7 +1246,7 @@ void P_ParticleEffect_f(void)
ptype->alpha = atof(value); ptype->alpha = atof(value);
else if (!strcmp(var, "alphachange")) else if (!strcmp(var, "alphachange"))
{ {
Con_DPrintf("alphachange is deprecated, use alphadelta\n"); Con_DPrintf("%s.%s: alphachange is deprecated, use alphadelta\n", ptype->config, ptype->name);
ptype->alphachange = atof(value); ptype->alphachange = atof(value);
} }
else if (!strcmp(var, "alphadelta")) else if (!strcmp(var, "alphadelta"))
@ -1268,7 +1271,7 @@ void P_ParticleEffect_f(void)
} }
else if (!strcmp(var, "diesubrand")) else if (!strcmp(var, "diesubrand"))
{ {
Con_DPrintf("diesubrand is deprecated, use die with two arguments\n"); Con_DPrintf("%s.%s: diesubrand is deprecated, use die with two arguments\n", ptype->config, ptype->name);
ptype->randdie = atof(value); ptype->randdie = atof(value);
} }
@ -1339,6 +1342,49 @@ void P_ParticleEffect_f(void)
ptype = &part_type[pnum]; ptype = &part_type[pnum];
ptype->inwater = assoc; ptype->inwater = assoc;
} }
else if (!strcmp(var, "underwater"))
{
ptype->flags |= PT_TRUNDERWATER;
parsefluid:
if ((ptype->flags & (PT_TRUNDERWATER|PT_TROVERWATER)) == (PT_TRUNDERWATER|PT_TROVERWATER))
{
ptype->flags &= ~PT_TRUNDERWATER;
Con_Printf("%s.%s: both over and under water\n", ptype->config, ptype->name);
}
if (Cmd_Argc() == 1)
ptype->fluidmask = FTECONTENTS_FLUID;
else
{
int i = Cmd_Argc();
ptype->fluidmask = 0;
while (i --> 1)
{
char *value = Cmd_Argv(i);
if (!strcmp(value, "water"))
ptype->fluidmask |= FTECONTENTS_WATER;
else if (!strcmp(value, "slime"))
ptype->fluidmask |= FTECONTENTS_SLIME;
else if (!strcmp(value, "lava"))
ptype->fluidmask |= FTECONTENTS_LAVA;
else if (!strcmp(value, "sky"))
ptype->fluidmask |= FTECONTENTS_SKY;
else if (!strcmp(value, "fluid"))
ptype->fluidmask |= FTECONTENTS_FLUID;
else if (!strcmp(value, "solid"))
ptype->fluidmask |= FTECONTENTS_SOLID;
else if (!strcmp(value, "playerclip"))
ptype->fluidmask |= FTECONTENTS_PLAYERCLIP;
else
Con_Printf("%s.%s: unknown contents: %s\n", ptype->config, ptype->name, value);
}
}
}
else if (!strcmp(var, "notunderwater"))
{
ptype->flags |= PT_TROVERWATER;
goto parsefluid;
}
else if (!strcmp(var, "model")) else if (!strcmp(var, "model"))
{ {
partmodels_t *mod; partmodels_t *mod;
@ -1623,7 +1669,7 @@ void P_ParticleEffect_f(void)
} }
else if (!strcmp(var, "isbeam")) else if (!strcmp(var, "isbeam"))
{ {
Con_DPrintf("isbeam is deprecated, use type beam\n"); Con_DPrintf("%s.%s: isbeam is deprecated, use type beam\n", ptype->config, ptype->name);
ptype->looks.type = PT_BEAM; ptype->looks.type = PT_BEAM;
} }
else if (!strcmp(var, "spawntime")) else if (!strcmp(var, "spawntime"))
@ -1662,22 +1708,22 @@ void P_ParticleEffect_f(void)
// old names // old names
else if (!strcmp(var, "areaspread")) else if (!strcmp(var, "areaspread"))
{ {
Con_DPrintf("areaspread is deprecated, use spawnorg\n"); Con_DPrintf("%s.%s: areaspread is deprecated, use spawnorg\n", ptype->config, ptype->name);
ptype->areaspread = atof(value); ptype->areaspread = atof(value);
} }
else if (!strcmp(var, "areaspreadvert")) else if (!strcmp(var, "areaspreadvert"))
{ {
Con_DPrintf("areaspreadvert is deprecated, use spawnorg\n"); Con_DPrintf("%s.%s: areaspreadvert is deprecated, use spawnorg\n", ptype->config, ptype->name);
ptype->areaspreadvert = atof(value); ptype->areaspreadvert = atof(value);
} }
else if (!strcmp(var, "offsetspread")) else if (!strcmp(var, "offsetspread"))
{ {
Con_DPrintf("offsetspread is deprecated, use spawnvel\n"); Con_DPrintf("%s.%s: offsetspread is deprecated, use spawnvel\n", ptype->config, ptype->name);
ptype->spawnvel = atof(value); ptype->spawnvel = atof(value);
} }
else if (!strcmp(var, "offsetspreadvert")) else if (!strcmp(var, "offsetspreadvert"))
{ {
Con_DPrintf("offsetspreadvert is deprecated, use spawnvel\n"); Con_DPrintf("%s.%s: offsetspreadvert is deprecated, use spawnvel\n", ptype->config, ptype->name);
ptype->spawnvelvert = atof(value); ptype->spawnvelvert = atof(value);
} }
@ -1713,7 +1759,7 @@ void P_ParticleEffect_f(void)
ptype->rampmode = RAMP_NONE; ptype->rampmode = RAMP_NONE;
else if (!strcmp(value, "absolute")) else if (!strcmp(value, "absolute"))
{ {
Con_DPrintf("'rampmode absolute' is deprecated, use 'rampmode nearest'\n"); Con_DPrintf("%s.%s: 'rampmode absolute' is deprecated, use 'rampmode nearest'\n", ptype->config, ptype->name);
ptype->rampmode = RAMP_NEAREST; ptype->rampmode = RAMP_NEAREST;
} }
else if (!strcmp(value, "nearest")) else if (!strcmp(value, "nearest"))
@ -1805,6 +1851,8 @@ void P_ParticleEffect_f(void)
ptype->rampindexes++; ptype->rampindexes++;
} }
else if (!strcmp(var, "viewspace"))
ptype->viewspacefrac = (Cmd_Argc()>1)?atof(value):1;
else if (!strcmp(var, "perframe")) else if (!strcmp(var, "perframe"))
ptype->flags |= PT_INVFRAMETIME; ptype->flags |= PT_INVFRAMETIME;
else if (!strcmp(var, "averageout")) else if (!strcmp(var, "averageout"))
@ -1819,7 +1867,7 @@ void P_ParticleEffect_f(void)
else if (!strcmp(var, "lightradius")) else if (!strcmp(var, "lightradius"))
{ //float version { //float version
ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value); ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value);
if (Cmd_Argc()>3) if (Cmd_Argc()>2)
ptype->dl_radius[1] = atof(Cmd_Argv(2)); ptype->dl_radius[1] = atof(Cmd_Argv(2));
ptype->dl_radius[1] -= ptype->dl_radius[0]; ptype->dl_radius[1] -= ptype->dl_radius[0];
} }
@ -1862,7 +1910,7 @@ void P_ParticleEffect_f(void)
ptype->stain_rgb[2] = atof(Cmd_Argv(4)); ptype->stain_rgb[2] = atof(Cmd_Argv(4));
} }
else else
Con_DPrintf("%s is not a recognised particle type field (in %s)\n", var, ptype->name); Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var);
} }
ptype->looks.invscalefactor = 1-ptype->looks.scalefactor; ptype->looks.invscalefactor = 1-ptype->looks.scalefactor;
ptype->loaded = part_parseweak?1:2; ptype->loaded = part_parseweak?1:2;
@ -1883,12 +1931,12 @@ void P_ParticleEffect_f(void)
if (ptype->scale) if (ptype->scale)
{ {
ptype->looks.type = PT_SPARKFAN; ptype->looks.type = PT_SPARKFAN;
Con_DPrintf("effect %s lacks a texture. assuming type sparkfan.\n", ptype->name); Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name);
} }
else else
{ {
ptype->looks.type = PT_SPARK; ptype->looks.type = PT_SPARK;
Con_DPrintf("effect %s lacks a texture. assuming type spark.\n", ptype->name); Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name);
} }
} }
else if (ptype->looks.type == PT_SPARK) else if (ptype->looks.type == PT_SPARK)
@ -1924,11 +1972,11 @@ void P_ParticleEffect_f(void)
if (ptype->rampmode && !ptype->ramp) if (ptype->rampmode && !ptype->ramp)
{ {
ptype->rampmode = RAMP_NONE; ptype->rampmode = RAMP_NONE;
Con_Printf("Particle type %s has a ramp mode but no ramp\n", ptype->name); Con_Printf("%s.%s: Particle has a ramp mode but no ramp\n", ptype->config, ptype->name);
} }
else if (ptype->ramp && !ptype->rampmode) else if (ptype->ramp && !ptype->rampmode)
{ {
Con_Printf("Particle type %s has a ramp but no ramp mode\n", ptype->name); Con_Printf("%s.%s: Particle has a ramp but no ramp mode\n", ptype->config, ptype->name);
} }
P_LoadTexture(ptype, true); P_LoadTexture(ptype, true);
@ -2108,6 +2156,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen)
if (ptype->areaspread || ptype->areaspreadvert) if (ptype->areaspread || ptype->areaspreadvert)
Q_strncatz(outstr, va("spawnorg %g %g\n", ptype->areaspread, ptype->areaspreadvert), outstrlen); Q_strncatz(outstr, va("spawnorg %g %g\n", ptype->areaspread, ptype->areaspreadvert), outstrlen);
if (ptype->viewspacefrac)
Q_strncatz(outstr, ((ptype->viewspacefrac==1)?"viewspace\n":va("viewspace %g\n", ptype->viewspacefrac)), outstrlen);
if (ptype->veladd || ptype->randomveladd) if (ptype->veladd || ptype->randomveladd)
{ {
if (ptype->randomveladd) if (ptype->randomveladd)
@ -3920,9 +3971,9 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
int cont; int cont;
cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org); cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org);
if ((ptype->flags & PT_TROVERWATER) && (cont & FTECONTENTS_FLUID)) if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
goto skip; goto skip;
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID)) if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
goto skip; goto skip;
} }
@ -4724,9 +4775,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype
int cont; int cont;
cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos); cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos);
if ((ptype->flags & PT_TROVERWATER) && (cont & FTECONTENTS_FLUID)) if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
return; return;
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID)) if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
return; return;
} }
@ -5853,6 +5904,8 @@ static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)
static void PScript_DrawParticleTypes (void) static void PScript_DrawParticleTypes (void)
{ {
float viewtranslation[16];
static float lastviewmatrix[16];
// void (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle; // void (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle;
void (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle; void (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle;
void (*sparktexturedparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTexturedSparkParticle; void (*sparktexturedparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTexturedSparkParticle;
@ -5924,6 +5977,14 @@ static void PScript_DrawParticleTypes (void)
kill_list = kill_first = NULL; kill_list = kill_first = NULL;
{
float tmp[16];
Matrix4_Invert(r_refdef.m_view, tmp);
Matrix4_Multiply(tmp, lastviewmatrix, viewtranslation);
memcpy(lastviewmatrix, r_refdef.m_view, sizeof(tmp));
}
for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun) for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun)
{ {
if (type->clippeddecals) if (type->clippeddecals)
@ -6254,6 +6315,15 @@ static void PScript_DrawParticleTypes (void)
p->vel[2] -= grav; p->vel[2] -= grav;
} }
if (type->viewspacefrac)
{
vec3_t tmp;
Matrix4x4_CM_Transform3(viewtranslation, p->org, tmp);
VectorInterpolate(p->org, type->viewspacefrac, tmp, p->org);
Matrix4x4_CM_Transform3x3(viewtranslation, p->vel, tmp);
VectorInterpolate(p->vel, type->viewspacefrac, tmp, p->vel);
}
p->angle += p->rotationspeed*pframetime; p->angle += p->rotationspeed*pframetime;
switch (type->rampmode) switch (type->rampmode)

View file

@ -540,15 +540,25 @@ void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
const char *sample = PR_GetStringOfs(prinst, OFS_PARM0); const char *sample = PR_GetStringOfs(prinst, OFS_PARM0);
sfx_t *sfx = S_PrecacheSound(sample); sfx_t *sfx = S_PrecacheSound(sample);
if (sfx && sfx->loadstate == SLS_LOADING)
COM_WorkerPartialSync(sfx, &sfx->loadstate, SLS_LOADING);
if (!sfx || sfx->loadstate != SLS_LOADED) if (!sfx || sfx->loadstate != SLS_LOADED)
G_FLOAT(OFS_RETURN) = 0; G_FLOAT(OFS_RETURN) = 0;
else else
{ {
sfxcache_t cachebuf, *cache; sfxcache_t cachebuf, *cache;
if (sfx->decoder.decodedata) if (sfx->decoder.querydata)
{
G_FLOAT(OFS_RETURN) = sfx->decoder.querydata(sfx, NULL);
return;
}
else if (sfx->decoder.decodedata)
cache = sfx->decoder.decodedata(sfx, &cachebuf, 0x7ffffffe, 0); cache = sfx->decoder.decodedata(sfx, &cachebuf, 0x7ffffffe, 0);
else else
cache = sfx->decoder.buf; cache = sfx->decoder.buf;
if (!cache)
G_FLOAT(OFS_RETURN) = 0;
else
G_FLOAT(OFS_RETURN) = (cache->soundoffset+cache->length) / (float)snd_speed; G_FLOAT(OFS_RETURN) = (cache->soundoffset+cache->length) / (float)snd_speed;
} }
} }

View file

@ -1864,6 +1864,9 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars
csqc_worldchanged = false; csqc_worldchanged = false;
Surf_NewMap(); Surf_NewMap();
CL_UpdateWindowTitle(); CL_UpdateWindowTitle();
World_RBE_Shutdown(&csqc_world);
World_RBE_Start(&csqc_world);
} }
if (cl.worldmodel) if (cl.worldmodel)

View file

@ -2327,6 +2327,15 @@ pbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc)
return crc == 10020; return crc == 10020;
} }
static int QDECL MP_PRFileSize (const char *path)
{
flocation_t loc;
if (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc))
return loc.len;
else
return -1;
}
double menutime; double menutime;
qboolean MP_Init (void) qboolean MP_Init (void)
{ {
@ -2350,7 +2359,7 @@ qboolean MP_Init (void)
menuprogparms.progsversion = PROGSTRUCT_VERSION; menuprogparms.progsversion = PROGSTRUCT_VERSION;
menuprogparms.ReadFile = COM_LoadStackFile;//char *(*ReadFile) (char *fname, void *buffer, int *len); menuprogparms.ReadFile = COM_LoadStackFile;//char *(*ReadFile) (char *fname, void *buffer, int *len);
menuprogparms.FileSize = COM_FileSize;//int (*FileSize) (char *fname); //-1 if file does not exist menuprogparms.FileSize = MP_PRFileSize;//int (*FileSize) (char *fname); //-1 if file does not exist
menuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len); menuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);
menuprogparms.Printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...); menuprogparms.Printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...);
menuprogparms.Sys_Error = Sys_Error; menuprogparms.Sys_Error = Sys_Error;

File diff suppressed because it is too large Load diff

View file

@ -302,7 +302,7 @@ void Surf_RenderAmbientLightmaps (struct msurface_s *fa, int ambient);
int Surf_LightmapShift (struct model_s *model); int Surf_LightmapShift (struct model_s *model);
#define LMBLOCK_SIZE_MAX 2048 //single axis #define LMBLOCK_SIZE_MAX 2048 //single axis
typedef struct glRect_s { typedef struct glRect_s {
unsigned short l,t,w,h; unsigned short l,t,r,b;
} glRect_t; } glRect_t;
typedef unsigned char stmap; typedef unsigned char stmap;
struct mesh_s; struct mesh_s;
@ -488,7 +488,7 @@ double Media_TweekCaptureFrameTime(double oldtime, double time);
void MYgluPerspective(double fovx, double fovy, double zNear, double zFar); void MYgluPerspective(double fovx, double fovy, double zNear, double zFar);
void R_PushDlights (void); void R_PushDlights (void);
qbyte *R_MarkLeaves_Q1 (void); qbyte *R_MarkLeaves_Q1 (qboolean getvisonly);
qbyte *R_CalcVis_Q1 (void); qbyte *R_CalcVis_Q1 (void);
qbyte *R_MarkLeaves_Q2 (void); qbyte *R_MarkLeaves_Q2 (void);
qbyte *R_MarkLeaves_Q3 (void); qbyte *R_MarkLeaves_Q3 (void);

View file

@ -112,7 +112,7 @@ cvar_t r_skin_overlays = SCVARF ("r_skin_overlays", "1",
cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count.");
cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are.");
cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends."); cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends.");
cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "1", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: BSP occlusion only.\n1: non-bsp occlusion also"); cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: No occlusion, at all.\n1: BSP occlusion only (simple tracelines).\n2: non-bsp occlusion also (complex tracelines).\n3: Depthbuffer reads (forces synchronisation).\n4: occlusion queries.");
cvar_t r_flashblend = SCVARF ("gl_flashblend", "0", cvar_t r_flashblend = SCVARF ("gl_flashblend", "0",
CVAR_ARCHIVE); CVAR_ARCHIVE);
cvar_t r_flashblendscale = SCVARF ("gl_flashblendscale", "0.35", cvar_t r_flashblendscale = SCVARF ("gl_flashblendscale", "0.35",
@ -398,7 +398,7 @@ cvar_t r_fastturbcolour = CVARFD ("r_fastturbcolour", "0.1 0.2 0.3", CVAR_A
cvar_t r_waterstyle = CVARFD ("r_waterstyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "Changes how water, and teleporters are drawn. Possible values are:\n0: fastturb-style block colour.\n1: regular q1-style water.\n2: refraction(ripply and transparent)\n3: refraction with reflection at an angle\n4: ripplemapped without reflections (requires particle effects)\n5: ripples+reflections"); cvar_t r_waterstyle = CVARFD ("r_waterstyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "Changes how water, and teleporters are drawn. Possible values are:\n0: fastturb-style block colour.\n1: regular q1-style water.\n2: refraction(ripply and transparent)\n3: refraction with reflection at an angle\n4: ripplemapped without reflections (requires particle effects)\n5: ripples+reflections");
cvar_t r_slimestyle = CVARFD ("r_slimestyle", "", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only slime. If empty, defers to r_waterstyle."); cvar_t r_slimestyle = CVARFD ("r_slimestyle", "", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only slime. If empty, defers to r_waterstyle.");
cvar_t r_lavastyle = CVARFD ("r_lavastyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle."); cvar_t r_lavastyle = CVARFD ("r_lavastyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle.");
cvar_t r_telestyle = CVARFD ("r_telestyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle."); cvar_t r_telestyle = CVARFD ("r_telestyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only teleporters. If empty, defers to r_waterstyle.");
cvar_t r_vertexdlights = CVARD ("r_vertexdlights", "0", "Determine model lighting with respect to nearby dlights. Poor-man's rtlights."); cvar_t r_vertexdlights = CVARD ("r_vertexdlights", "0", "Determine model lighting with respect to nearby dlights. Poor-man's rtlights.");
@ -2209,7 +2209,7 @@ qbyte *R_CalcVis_Q1 (void)
} }
#endif #endif
qbyte *R_MarkLeaves_Q1 (void) qbyte *R_MarkLeaves_Q1 (qboolean getvisonly)
{ {
static qbyte *cvis[R_MAX_RECURSE]; static qbyte *cvis[R_MAX_RECURSE];
qbyte *vis; qbyte *vis;
@ -2271,7 +2271,9 @@ qbyte *R_MarkLeaves_Q1 (void)
r_visframecount++; r_visframecount++;
if (r_viewleaf && r_viewleaf->contents == Q1CONTENTS_SOLID) if (getvisonly)
return vis;
else if (r_viewleaf && r_viewleaf->contents == Q1CONTENTS_SOLID)
{ {
//to improve spectating, when the camera is in a wall, we ignore any sky leafs. //to improve spectating, when the camera is in a wall, we ignore any sky leafs.
//this prevents seeing the upwards-facing sky surfaces within the sky volumes. //this prevents seeing the upwards-facing sky surfaces within the sky volumes.

View file

@ -2165,7 +2165,7 @@ void Sbar_DrawScoreboard (void)
for (pnum = 0; pnum < cl.splitclients; pnum++) for (pnum = 0; pnum < cl.splitclients; pnum++)
{ {
if (cl.spectator) if (cl.spectator && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV))
{ {
int t = cl.playerview[pnum].cam_spec_track; int t = cl.playerview[pnum].cam_spec_track;
if (t < 0 || !CAM_ISLOCKED(&cl.playerview[pnum])) if (t < 0 || !CAM_ISLOCKED(&cl.playerview[pnum]))

256
engine/client/snd_xaudio.c Normal file
View file

@ -0,0 +1,256 @@
#include "quakedef.h"
//frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved.
//I suppose it has a use with WINRT... although that doesn't make it useful.
//we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies.
#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY)
#include "winquake.h"
#include <xaudio2.h>
#define SDRVNAME "XAudio2"
typedef struct
{
IXAudio2VoiceCallback cb; //must be first. yay for fake single inheritance.
IXAudio2 *ixa;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *source;
//contiguous block of memory, because its easier.
qbyte *bufferstart;
unsigned int subbuffersize; //in samplepairs
unsigned int buffercount;
unsigned int bufferidx;
unsigned int bufferavail;
} xaud_t;
static void *XAUDIO_Lock(soundcardinfo_t *sc, unsigned int *startoffset)
{
qbyte *ret;
xaud_t *xa = sc->handle;
ret = xa->bufferstart;
// *startoffset = 0;
// ret += xa->subbuffersize * xa->bufferidx;
return ret;
}
static void XAUDIO_Unlock(soundcardinfo_t *sc, void *buffer)
{
}
static unsigned int XAUDIO_GetDMAPos(soundcardinfo_t *sc)
{
xaud_t *xa = sc->handle;
unsigned int s = (xa->bufferidx+xa->bufferavail) * xa->subbuffersize * sc->sn.numchannels;
return s;
}
static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)
{
xaud_t *xa = sc->handle;
XAUDIO2_BUFFER buf;
//determine total buffer size
int buffersize = sc->sn.samples*sc->sn.samplebits/8;
//determine time offsets in bytes
start *= sc->sn.numchannels*sc->sn.samplebits/8;
end *= sc->sn.numchannels*sc->sn.samplebits/8;
while (start < end)
{
if (!xa->bufferavail)
break; //o.O that's not meant to happen
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = end - start;
if (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8)
{
if (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8)
{ //dma code should ensure that only multiples of 'samplequeue' are processed.
Con_Printf("XAudio2 underrun\n");
break;
}
buf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8;
}
buf.pAudioData = xa->bufferstart + (start%buffersize);
if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize)
{ //this shouldn't ever happen either
Con_Printf("XAudio2 overrun\n");
break;
}
IXAudio2SourceVoice_SubmitSourceBuffer(xa->source, &buf, NULL);
xa->bufferidx += 1;
xa->bufferavail -= 1;
start += buf.AudioBytes;
}
}
static void XAUDIO_Shutdown(soundcardinfo_t *sc)
{
xaud_t *xa = sc->handle;
//releases are allowed to block, supposedly.
IXAudio2SourceVoice_DestroyVoice(xa->source);
IXAudio2MasteringVoice_DestroyVoice(xa->master);
IXAudio2_Release(xa->ixa);
BZ_Free(xa->bufferstart);
Z_Free(xa);
sc->handle = NULL;
}
void WINAPI XAUDIO_CB_OnVoiceProcessingPassStart (IXAudio2VoiceCallback *ths, UINT32 BytesRequired) {}
void WINAPI XAUDIO_CB_OnVoiceProcessingPassEnd (IXAudio2VoiceCallback *ths) {}
void WINAPI XAUDIO_CB_OnStreamEnd (IXAudio2VoiceCallback *ths) {}
void WINAPI XAUDIO_CB_OnBufferStart (IXAudio2VoiceCallback *ths, void* pBufferContext) {}
void WINAPI XAUDIO_CB_OnBufferEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {xaud_t *xa = (xaud_t*)ths; S_LockMixer(); xa->bufferavail+=1; S_UnlockMixer();}
void WINAPI XAUDIO_CB_OnLoopEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {}
void WINAPI XAUDIO_CB_OnVoiceError (IXAudio2VoiceCallback *ths, void* pBufferContext, HRESULT Error) {}
static IXAudio2VoiceCallbackVtbl cbvtbl =
{
XAUDIO_CB_OnVoiceProcessingPassStart,
XAUDIO_CB_OnVoiceProcessingPassEnd,
XAUDIO_CB_OnStreamEnd,
XAUDIO_CB_OnBufferStart,
XAUDIO_CB_OnBufferEnd,
XAUDIO_CB_OnLoopEnd,
XAUDIO_CB_OnVoiceError
};
static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
{
#ifdef WINRT
char *dev = NULL;
#else
int dev = 0;
#endif
xaud_t *xa = Z_Malloc(sizeof(*xa));
WAVEFORMATEX wfmt;
xa->cb.lpVtbl = &cbvtbl;
sc->sn.numchannels = 2;
wfmt.wFormatTag = WAVE_FORMAT_PCM;
wfmt.nChannels = sc->sn.numchannels;
wfmt.nSamplesPerSec = sc->sn.speed;
wfmt.wBitsPerSample = sc->sn.samplebits;
wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
wfmt.cbSize = 0;
sc->inactive_sound = true;
xa->buffercount = xa->bufferavail = 3; //submit this many straight up
xa->subbuffersize = 256; //number of sampleblocks per submission
sc->samplequeue = -1; //-1 means we're streaming, XAUDIO_GetDMAPos returns exactly as much as we want to paint to.
sc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels;
xa->bufferstart = BZ_Malloc(sc->sn.samples * (sc->sn.samplebits/8));
if (xa->bufferstart)
{
if (SUCCEEDED(XAudio2Create(&xa->ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
#ifdef WINRT
if (SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL, AudioCategory_GameEffects)))
#else
if (cardname && *cardname)
{
UINT32 devs = 0;
XAUDIO2_DEVICE_DETAILS details;
char id[MAX_QPATH];
if (FAILED(IXAudio2_GetDeviceCount(xa->ixa, &devs)))
devs = 0;
for (dev = 0; dev < devs; dev++)
{
if (SUCCEEDED(IXAudio2_GetDeviceDetails(xa->ixa, dev, &details)))
{
narrowen(id, sizeof(id), details.DeviceID);
if (!strcmp(id, cardname))
break;
}
}
if (dev == devs)
dev = ~0; //something invalid.
}
/*
//FIXME: correct the details to match the hardware
*/
if (dev != ~0 && SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL)))
#endif
{
//egads
XAUDIO2_VOICE_SENDS vs;
XAUDIO2_SEND_DESCRIPTOR sd[1];
vs.SendCount = 1;
vs.pSends = sd;
sd[0].Flags = 0;
sd[0].pOutputVoice = (IXAudio2Voice*)xa->master;
if (SUCCEEDED(IXAudio2_CreateSourceVoice(xa->ixa, &xa->source, &wfmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &xa->cb, &vs, NULL)))
{
sc->handle = xa;
sc->GetDMAPos = XAUDIO_GetDMAPos;
sc->Lock = XAUDIO_Lock;
sc->Unlock = XAUDIO_Unlock;
sc->Submit = XAUDIO_Submit;
sc->Shutdown = XAUDIO_Shutdown;
IXAudio2SourceVoice_Start(xa->source, 0, XAUDIO2_COMMIT_NOW);
return true;
}
IXAudio2MasteringVoice_DestroyVoice(xa->master);
}
IXAudio2_Release(xa->ixa);
}
BZ_Free(xa->bufferstart);
}
Z_Free(xa);
return false;
}
qboolean QDECL XAUDIO_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))
{
IXAudio2 *ixa;
#ifndef WINRT
CoInitializeEx(NULL, COINIT_MULTITHREADED);
//winrt provides no enumeration mechanism.
if (SUCCEEDED(XAudio2Create(&ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
UINT32 devs = 0, i;
XAUDIO2_DEVICE_DETAILS details;
char id[MAX_QPATH], name[MAX_QPATH];
if (FAILED(IXAudio2_GetDeviceCount(ixa, &devs)))
devs = 0;
strcpy(name, "XA2:");
for (i = 0; i < devs; i++)
{
if (SUCCEEDED(IXAudio2_GetDeviceDetails(ixa, i, &details)))
{
narrowen(id, sizeof(id), details.DeviceID);
narrowen(name+4, sizeof(name)-4, details.DisplayName);
callback(SDRVNAME, id, name);
}
}
IXAudio2_Release(ixa);
return true;
}
#endif
return false;
}
sounddriver_t XAUDIO2_Output =
{
SDRVNAME,
XAUDIO_InitCard,
XAUDIO_Enumerate
};
#endif

View file

@ -25,12 +25,6 @@ static void Headless_IMG_UpdateFiltering (image_t *imagelist, int filtermip[3],
} }
static qboolean Headless_IMG_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips) static qboolean Headless_IMG_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips)
{ {
int i;
for (i = 0; i < mips->mipcount; i++)
if (mips->mip[i].needfree)
Z_Free(mips->mip[i].data);
if (mips->extrafree)
Z_Free(mips->extrafree);
return true; return true;
} }
static void Headless_IMG_DestroyTexture (texid_t tex) static void Headless_IMG_DestroyTexture (texid_t tex)
@ -141,6 +135,8 @@ static qboolean Headless_VID_Init (rendererstate_t *info, unsigned char *pale
Shell_NotifyIconA(NIM_ADD, &data); Shell_NotifyIconA(NIM_ADD, &data);
} }
#endif #endif
memset(&sh_config, 0, sizeof(sh_config));
return true; return true;
} }
static void Headless_VID_DeInit (void) static void Headless_VID_DeInit (void)
@ -196,7 +192,7 @@ static struct batch_s *Headless_BE_GetTempBatch (void)
{ {
return NULL; return NULL;
} }
static void Headless_BE_DrawWorld (qboolean drawworld, qbyte *vis) static void Headless_BE_DrawWorld (struct batch_s **worldbatches, qbyte *vis)
{ {
} }
static void Headless_BE_Init (void) static void Headless_BE_Init (void)

View file

@ -244,6 +244,14 @@ void SwapPic (qpic_t *pic)
//FIXME: convert to linked list. is hunk possible? //FIXME: convert to linked list. is hunk possible?
//hash tables? //hash tables?
#define TEXWAD_MAXIMAGES 16384 #define TEXWAD_MAXIMAGES 16384
typedef struct wadfile_s
{
vfsfile_t *file;
struct wadfile_s *next;
char name[1];
} wadfile_t;
typedef struct typedef struct
{ {
char name[16]; char name[16];
@ -253,18 +261,15 @@ typedef struct
} texwadlump_t; } texwadlump_t;
int numwadtextures; int numwadtextures;
static texwadlump_t texwadlump[TEXWAD_MAXIMAGES]; static texwadlump_t texwadlump[TEXWAD_MAXIMAGES];
void *wadmutex;
typedef struct wadfile_s {
vfsfile_t *file;
struct wadfile_s *next;
char name[1];
} wadfile_t;
wadfile_t *openwadfiles; wadfile_t *openwadfiles;
void Wads_Flush (void) void Wads_Flush (void)
{ {
wadfile_t *wf; wadfile_t *wf;
if (wadmutex)
Sys_LockMutex(wadmutex);
while(openwadfiles) while(openwadfiles)
{ {
VFS_CLOSE(openwadfiles->file); VFS_CLOSE(openwadfiles->file);
@ -275,6 +280,8 @@ void Wads_Flush (void)
} }
numwadtextures=0; numwadtextures=0;
if (wadmutex)
Sys_UnlockMutex(wadmutex);
} }
/* /*
==================== ====================
@ -411,12 +418,23 @@ qbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *hei
tex->offsets[1] == tex->offsets[0] + (tex->width)*(tex->height) && tex->offsets[1] == tex->offsets[0] + (tex->width)*(tex->height) &&
tex->offsets[2] == tex->offsets[1] + (tex->width>>1)*(tex->height>>1) && tex->offsets[2] == tex->offsets[1] + (tex->width>>1)*(tex->height>>1) &&
tex->offsets[3] == tex->offsets[2] + (tex->width>>2)*(tex->height>>2) && tex->offsets[3] == tex->offsets[2] + (tex->width>>2)*(tex->height>>2) &&
lumpsize == tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2 + 768) ((lumpsize+3)&~3) >= ((tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2 + 768+3)&~3))
pal = (qbyte *)tex + tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2; pal = (qbyte *)tex + tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2;
else else
pal = host_basepal; pal = host_basepal;
if (tex->offsets[0] + tex->width * tex->height > lumpsize)
{ //fucked texture.
for (d = 0;d < tex->width * tex->height;d++) for (d = 0;d < tex->width * tex->height;d++)
{
out[0] = 0;
out[1] = 255;
out[2] = 0;
out[3] = 255;
out += 4;
}
}
else for (d = 0;d < tex->width * tex->height;d++)
{ {
p = *in++; p = *in++;
if (alpha==1 && p == 255) //only allow alpha on '{' textures if (alpha==1 && p == 255) //only allow alpha on '{' textures
@ -473,20 +491,19 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp
texname[16] = 0; texname[16] = 0;
W_CleanupName (name, texname); W_CleanupName (name, texname);
Sys_LockMutex(wadmutex);
for (i = 0;i < numwadtextures;i++) for (i = 0;i < numwadtextures;i++)
{ {
if (!strcmp(texname, texwadlump[i].name)) // found it if (!strcmp(texname, texwadlump[i].name)) // found it
{ {
file = texwadlump[i].file; file = texwadlump[i].file;
if (!VFS_SEEK(file, texwadlump[i].position))
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;}
if (VFS_SEEK(file, texwadlump[i].position))
{
tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like
if (!tex) if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)
return NULL; {
if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size) Sys_UnlockMutex(wadmutex);
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;}
tex->width = LittleLong(tex->width); tex->width = LittleLong(tex->width);
tex->height = LittleLong(tex->height); tex->height = LittleLong(tex->height);
for (j = 0;j < MIPLEVELS;j++) for (j = 0;j < MIPLEVELS;j++)
@ -497,6 +514,11 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp
return data; return data;
} }
} }
Con_Printf("W_GetTexture: corrupt WAD3 file\n");
break;
}
}
Sys_UnlockMutex(wadmutex);
return NULL; return NULL;
} }
@ -509,20 +531,18 @@ miptex_t *W_GetMipTex(const char *name)
texname[16] = 0; texname[16] = 0;
W_CleanupName (name, texname); W_CleanupName (name, texname);
Sys_LockMutex(wadmutex);
for (i = 0;i < numwadtextures;i++) for (i = 0;i < numwadtextures;i++)
{ {
if (!strcmp(texname, texwadlump[i].name)) // found it if (!strcmp(texname, texwadlump[i].name)) // found it
{ {
file = texwadlump[i].file; file = texwadlump[i].file;
if (!VFS_SEEK(file, texwadlump[i].position)) if (VFS_SEEK(file, texwadlump[i].position))
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;} {
tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like
if (!tex) if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)
return NULL; {
if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size) Sys_UnlockMutex(wadmutex);
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;}
tex->width = LittleLong(tex->width); tex->width = LittleLong(tex->width);
tex->height = LittleLong(tex->height); tex->height = LittleLong(tex->height);
for (j = 0;j < MIPLEVELS;j++) for (j = 0;j < MIPLEVELS;j++)
@ -530,6 +550,11 @@ miptex_t *W_GetMipTex(const char *name)
return tex; return tex;
} }
} }
Con_Printf("W_GetTexture: corrupt WAD3 file\n");
break;
}
}
Sys_UnlockMutex(wadmutex);
return NULL; return NULL;
} }
@ -689,12 +714,14 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
key[3] = ' '; key[3] = ' ';
Q_strncpyz(key+4, token, sizeof(key)-4); Q_strncpyz(key+4, token, sizeof(key)-4);
Cbuf_AddText(key, RESTRICT_INSECURE); Cbuf_AddText(key, RESTRICT_INSECURE);
Cbuf_AddText("\n", RESTRICT_INSECURE);
} }
else if (!strcmp("waterfog", key)) //q1 extension. FIXME: should be made temporary. else if (!strcmp("waterfog", key)) //q1 extension. FIXME: should be made temporary.
{ {
memcpy(key, "waterfog ", 9); memcpy(key, "waterfog ", 9);
Q_strncpyz(key+9, token, sizeof(key)-9); Q_strncpyz(key+9, token, sizeof(key)-9);
Cbuf_AddText(key, RESTRICT_INSECURE); Cbuf_AddText(key, RESTRICT_INSECURE);
Cbuf_AddText("\n", RESTRICT_INSECURE);
} }
else if (!strncmp("cvar_", key, 5)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines). else if (!strncmp("cvar_", key, 5)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines).
{ {

View file

@ -93,6 +93,7 @@ void *W_GetLumpName (char *name);
void *W_SafeGetLumpName (const char *name); void *W_SafeGetLumpName (const char *name);
void *W_GetLumpNum (int num); void *W_GetLumpNum (int num);
void Wads_Flush (void); void Wads_Flush (void);
extern void *wadmutex;
void SwapPic (qpic_t *pic); void SwapPic (qpic_t *pic);

View file

@ -3546,7 +3546,7 @@ qbool TP_CheckSoundTrigger (char *str)
COM_DefaultExtension (soundname, ".wav", sizeof(soundname)); COM_DefaultExtension (soundname, ".wav", sizeof(soundname));
// make sure we have it on disk (FIXME) // make sure we have it on disk (FIXME)
if (!FS_FLocateFile (va("sound/%s", soundname), FSLFRT_IFFOUND, NULL)) if (!FS_FLocateFile (va("sound/%s", soundname), FSLF_IFFOUND, NULL))
return false; return false;
// now play the sound // now play the sound

View file

@ -179,7 +179,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef AVAIL_JPEGLIB #undef AVAIL_JPEGLIB
#undef AVAIL_XZDEC #undef AVAIL_XZDEC
#if defined(_WIN32) && !defined(FTE_SDL) !defined(MULTITHREAD) //always thread on win32 non-minimal builds #if defined(_WIN32) && !defined(FTE_SDL) && !defined(MULTITHREAD) //always thread on win32 non-minimal builds
#define MULTITHREAD #define MULTITHREAD
#endif #endif
#elif defined(MINIMAL) #elif defined(MINIMAL)

View file

@ -616,7 +616,7 @@ void Cmd_Exec_f (void)
else else
Q_strncpyz(name, Cmd_Argv(1), sizeof(name)); Q_strncpyz(name, Cmd_Argv(1), sizeof(name));
if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLFRT_IFFOUND, &loc)) if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLF_IFFOUND, &loc))
{ {
Con_TPrintf ("couldn't exec %s\n", name); Con_TPrintf ("couldn't exec %s\n", name);
return; return;

View file

@ -984,7 +984,7 @@ void R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int
} }
} }
if (r_vertexdlights.ival && r_dynamic.ival) if (r_vertexdlights.ival && r_dynamic.ival > 0)
{ {
unsigned int lno, v; unsigned int lno, v;
vec3_t dir, rel; vec3_t dir, rel;
@ -2787,13 +2787,11 @@ void Mod_LoadAliasShaders(model_t *mod)
//Q1 model loading //Q1 model loading
#if 1 #if 1
static galiasinfo_t *galias;
static dmdl_t *pq1inmodel;
#define NUMVERTEXNORMALS 162 #define NUMVERTEXNORMALS 162
extern float r_avertexnormals[NUMVERTEXNORMALS][3]; extern float r_avertexnormals[NUMVERTEXNORMALS][3];
// mdltype 0 = q1, 1 = qtest, 2 = rapo/h2 // mdltype 0 = q1, 1 = qtest, 2 = rapo/h2
static void Alias_LoadPose(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) static void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype)
{ {
int j; int j;
#ifdef HEXEN2 #ifdef HEXEN2
@ -2830,7 +2828,7 @@ static void Alias_LoadPose(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t
} }
} }
} }
static void Alias_LoadPose16(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) static void Q1MDL_LoadPose16(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype)
{ {
//quakeforge's MD16 format has regular 8bit stuff, trailed by an extra low-order set of the verts providing the extra 8bits of precision. //quakeforge's MD16 format has regular 8bit stuff, trailed by an extra low-order set of the verts providing the extra 8bits of precision.
//its worth noting that the model could be rendered using the high-order parts only, if your software renderer only supports that or whatever. //its worth noting that the model could be rendered using the high-order parts only, if your software renderer only supports that or whatever.
@ -2855,7 +2853,7 @@ static void Alias_LoadPose16(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_
} }
} }
} }
static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype) static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype)
{ {
galiaspose_t *pose; galiaspose_t *pose;
galiasanimation_t *frame = galias->ofsanimations; galiasanimation_t *frame = galias->ofsanimations;
@ -2910,12 +2908,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
if (mdltype & 16) if (mdltype & 16)
{ {
Alias_LoadPose16(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); Q1MDL_LoadPose16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts*2]; pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts*2];
} }
else else
{ {
Alias_LoadPose(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts]; pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts];
} }
@ -2972,12 +2970,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
if (mdltype & 16) if (mdltype & 16)
{ {
Alias_LoadPose16(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); Q1MDL_LoadPose16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
pinframe += pq1inmodel->numverts*2; pinframe += pq1inmodel->numverts*2;
} }
else else
{ {
Alias_LoadPose(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
pinframe += pq1inmodel->numverts; pinframe += pq1inmodel->numverts;
} }
@ -3007,7 +3005,7 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
//greatly reduced version of Q1_LoadSkins //greatly reduced version of Q1_LoadSkins
//just skips over the data //just skips over the data
static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintranstype) static void *Q1MDL_LoadSkins_SV (galiasinfo_t *galias, dmdl_t *pq1inmodel, daliasskintype_t *pskintype, unsigned int skintranstype)
{ {
int i; int i;
int s; int s;
@ -3038,7 +3036,7 @@ static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintran
} }
#ifndef SERVERONLY #ifndef SERVERONLY
static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype) static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype)
{ {
skinframe_t *frames; skinframe_t *frames;
char skinname[MAX_QPATH]; char skinname[MAX_QPATH];
@ -3362,8 +3360,8 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
dh2triangle_t *pinh2triangles; dh2triangle_t *pinh2triangles;
qboolean rapo = false; qboolean rapo = false;
#endif #endif
galiasinfo_t *galias;
pq1inmodel = (dmdl_t *)buffer; dmdl_t *pq1inmodel = (dmdl_t *)buffer;
hdrsize = sizeof(dmdl_t) - sizeof(int); hdrsize = sizeof(dmdl_t) - sizeof(int);
@ -3450,11 +3448,11 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
{ {
default: default:
#ifndef SERVERONLY #ifndef SERVERONLY
pinstverts = (dstvert_t *)Q1_LoadSkins_GL(mod, skinstart, skintranstype); pinstverts = (dstvert_t *)Q1MDL_LoadSkins_GL(galias, pq1inmodel, mod, skinstart, skintranstype);
break; break;
#endif #endif
case QR_NONE: case QR_NONE:
pinstverts = (dstvert_t *)Q1_LoadSkins_SV(skinstart, skintranstype); pinstverts = (dstvert_t *)Q1MDL_LoadSkins_SV(galias, pq1inmodel, skinstart, skintranstype);
break; break;
} }
@ -3535,7 +3533,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
#endif #endif
end = &pinh2triangles[pq1inmodel->numtris]; end = &pinh2triangles[pq1inmodel->numtris];
if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, 2) == NULL) if (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, 2) == NULL)
{ {
BZ_Free(seamremap); BZ_Free(seamremap);
ZG_FreeGroup(&mod->memgroup); ZG_FreeGroup(&mod->memgroup);
@ -3612,7 +3610,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
end = &pinq1triangles[pq1inmodel->numtris]; end = &pinq1triangles[pq1inmodel->numtris];
//frames //frames
if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, (sixteenbit?16:0) | (qtest?1:0)) == NULL) if (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, (sixteenbit?16:0) | (qtest?1:0)) == NULL)
{ {
BZ_Free(seamremap); BZ_Free(seamremap);
ZG_FreeGroup(&mod->memgroup); ZG_FreeGroup(&mod->memgroup);
@ -3706,7 +3704,7 @@ typedef struct
#define Q2NUMVERTEXNORMALS 162 #define Q2NUMVERTEXNORMALS 162
extern vec3_t bytedirs[Q2NUMVERTEXNORMALS]; extern vec3_t bytedirs[Q2NUMVERTEXNORMALS];
static void Q2_LoadSkins(model_t *mod, md2_t *pq2inmodel, char *skins) static void Q2MD2_LoadSkins(galiasinfo_t *galias, model_t *mod, md2_t *pq2inmodel, char *skins)
{ {
#ifndef SERVERONLY #ifndef SERVERONLY
int i; int i;
@ -3777,6 +3775,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize)
int numverts; int numverts;
int size; int size;
galiasinfo_t *galias;
mod->engineflags |= MDLF_NEEDOVERBRIGHT; mod->engineflags |= MDLF_NEEDOVERBRIGHT;
@ -3820,7 +3819,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize)
galias->nextsurf = 0; galias->nextsurf = 0;
//skins //skins
Q2_LoadSkins(mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins))); Q2MD2_LoadSkins(galias, mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins)));
//trianglelists; //trianglelists;
pintri = (dmd2triangle_t *)((char *)pq2inmodel + LittleLong(pq2inmodel->ofs_tris)); pintri = (dmd2triangle_t *)((char *)pq2inmodel + LittleLong(pq2inmodel->ofs_tris));
@ -4604,6 +4603,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize)
md3Header_t *header; md3Header_t *header;
md3Surface_t *surf; md3Surface_t *surf;
galiasinfo_t *galias;
header = buffer; header = buffer;

View file

@ -4724,11 +4724,24 @@ void COM_ErrorMe_f(void)
#ifdef LOADERTHREAD #ifdef LOADERTHREAD
#define WORKERTHREADS (1+1+4) #define WG_MAIN 0
#define WG_LOADER 1
#define WG_COUNT 2 //main and loaders
#define WORKERTHREADS 16 //max
/*multithreading worker thread stuff*/ /*multithreading worker thread stuff*/
static void *com_workercondition[WORKERTHREADS]; static int com_liveworkers[WG_COUNT];
static qboolean com_workerdone[WORKERTHREADS]; static void *com_workercondition[WG_COUNT];
static void *com_workerthread[WORKERTHREADS]; static volatile int com_workeracksequence;
static struct com_worker_s
{
void *thread;
volatile enum {
WR_NONE,
WR_DIE,
WR_ACK //updates ackseq to com_workeracksequence and sends a signal to WG_MAIN
} request;
volatile int ackseq;
} com_worker[WORKERTHREADS];
static unsigned int mainthreadid; static unsigned int mainthreadid;
qboolean com_workererror; qboolean com_workererror;
static struct com_work_s static struct com_work_s
@ -4739,77 +4752,12 @@ static struct com_work_s
void *data; void *data;
size_t a; size_t a;
size_t b; size_t b;
} *com_work_head[WORKERTHREADS], *com_work_tail[WORKERTHREADS]; } *com_work_head[WG_COUNT], *com_work_tail[WG_COUNT];
static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b)
{
Sys_Error(data);
}
void COM_WorkerAbort(char *message)
{
int us;
struct com_work_s work;
if (Sys_IsMainThread())
return;
com_workererror = true;
if (!com_workercondition[0])
return; //Sys_IsMainThread was probably called too early...
memset(&work, 0, sizeof(work));
work.func = Sys_ErrorThread;
work.ctx = NULL;
work.data = message;
work.a = 0;
work.b = 0;
Sys_LockConditional(com_workercondition[0]);
if (com_work_tail[0])
{
com_work_tail[0]->next = &work;
com_work_tail[0] = &work;
}
else
com_work_head[0] = com_work_tail[0] = &work;
Sys_ConditionSignal(com_workercondition[0]);
Sys_UnlockConditional(com_workercondition[0]);
//find out which worker we are
for (us = WORKERTHREADS-1; us > 0; us--)
if (Sys_IsThread(com_workerthread[us]))
break;
if (us)
{
//and post any pending work we have back over to the main thread, because we're going down as soon as we can.
while(!com_workerdone[us])
{
struct com_work_s *w;
Sys_LockConditional(com_workercondition[us]);
w = com_work_head[us];
if (w)
com_work_head[us] = w->next;
if (!com_work_head[us])
com_work_head[us] = com_work_tail[us] = NULL;
Sys_UnlockConditional(com_workercondition[us]);
if (w)
{
COM_AddWork(0, w->func, w->ctx, w->data, w->a, w->b);
Z_Free(w);
}
else
Sys_ConditionSignal(com_workercondition[0]);
Sys_Sleep(0.1);
}
com_workerdone[0] = true;
Sys_ConditionSignal(com_workercondition[0]);
}
Sys_ThreadAbort();
}
//return if there's *any* loading that needs to be done anywhere. //return if there's *any* loading that needs to be done anywhere.
qboolean COM_HasWork(void) qboolean COM_HasWork(void)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < WORKERTHREADS-1; i++) for (i = 0; i < WG_COUNT; i++)
{ {
if (com_work_head[i]) if (com_work_head[i])
return true; return true;
@ -4817,12 +4765,15 @@ qboolean COM_HasWork(void)
return false; return false;
} }
//thread==0 is main thread, thread==1 is a worker thread //thread==0 is main thread, thread==1 is a worker thread
void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) void COM_AddWork(int tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b)
{ {
struct com_work_s *work; struct com_work_s *work;
if (tg >= WG_COUNT)
return;
//no worker there, just do it immediately on this thread instead of pushing it to the worker. //no worker there, just do it immediately on this thread instead of pushing it to the worker.
if (thread && (!com_workerthread[thread] || com_workererror)) if (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror))
{ {
func(ctx, data, a, b); func(ctx, data, a, b);
return; return;
@ -4837,19 +4788,19 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t
work->b = b; work->b = b;
//queue it (fifo) //queue it (fifo)
Sys_LockConditional(com_workercondition[thread]); Sys_LockConditional(com_workercondition[tg]);
if (com_work_tail[thread]) if (com_work_tail[tg])
{ {
com_work_tail[thread]->next = work; com_work_tail[tg]->next = work;
com_work_tail[thread] = work; com_work_tail[tg] = work;
} }
else else
com_work_head[thread] = com_work_tail[thread] = work; com_work_head[tg] = com_work_tail[tg] = work;
// Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); // Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?");
Sys_ConditionSignal(com_workercondition[thread]); Sys_ConditionSignal(com_workercondition[tg]);
Sys_UnlockConditional(com_workercondition[thread]); Sys_UnlockConditional(com_workercondition[tg]);
// if (!com_workerthread[thread]) // if (!com_workerthread[thread])
// while(COM_DoWork(thread, false)) // while(COM_DoWork(thread, false))
@ -4857,72 +4808,157 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t
} }
//leavelocked = false == poll mode. //leavelocked = false == poll mode.
//leavelocked = true == safe sleeping //leavelocked = true == safe sleeping
qboolean COM_DoWork(int thread, qboolean leavelocked) qboolean COM_DoWork(int tg, qboolean leavelocked)
{ {
struct com_work_s *work; struct com_work_s *work;
if (tg >= WG_COUNT)
return false;
if (!leavelocked) if (!leavelocked)
{ {
//skip the locks if it looks like we can be lazy. //skip the locks if it looks like we can be lazy.
if (!com_work_head[thread]) if (!com_work_head[tg])
return false; return false;
Sys_LockConditional(com_workercondition[thread]); Sys_LockConditional(com_workercondition[tg]);
} }
work = com_work_head[thread]; work = com_work_head[tg];
if (work) if (work)
com_work_head[thread] = work->next; com_work_head[tg] = work->next;
if (!com_work_head[thread]) if (!com_work_head[tg])
com_work_head[thread] = com_work_tail[thread] = NULL; com_work_head[tg] = com_work_tail[tg] = NULL;
if (work) if (work)
{ {
// Sys_Printf("%x: Doing work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); // Sys_Printf("%x: Doing work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?");
Sys_UnlockConditional(com_workercondition[thread]); Sys_UnlockConditional(com_workercondition[tg]);
work->func(work->ctx, work->data, work->a, work->b); work->func(work->ctx, work->data, work->a, work->b);
Z_Free(work); Z_Free(work);
if (leavelocked) if (leavelocked)
Sys_LockConditional(com_workercondition[thread]); Sys_LockConditional(com_workercondition[tg]);
return true; //did something, check again return true; //did something, check again
} }
if (!leavelocked) if (!leavelocked)
Sys_UnlockConditional(com_workercondition[thread]); Sys_UnlockConditional(com_workercondition[tg]);
//nothing going on, if leavelocked then noone can add anything until we sleep. //nothing going on, if leavelocked then noone can add anything until we sleep.
return false; return false;
} }
static void COM_WorkerSync_StopWorker(void *ctx, void *data, size_t a, size_t b) static void COM_WorkerSync_ThreadAck(void *ctx, void *data, size_t a, size_t b)
{ {
com_workerdone[a] = true; int us;
int *ackbuf = ctx;
Sys_LockConditional(com_workercondition[WG_MAIN]);
//find out which worker we are, and flag ourselves as having acked the main thread to clean us up
for (us = 0; us < WORKERTHREADS; us++)
{
if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))
{
ackbuf[us] = true;
break;
} }
static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b) }
*(int*)data += 1;
//and tell the main thread it can stop being idle now
Sys_ConditionSignal(com_workercondition[WG_MAIN]);
Sys_UnlockConditional(com_workercondition[WG_MAIN]);
}
/*static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b)
{ {
Sys_LockConditional(com_workercondition[a]); Sys_LockConditional(com_workercondition[a]);
com_workerdone[a] = true; com_workerdone[a] = true;
Sys_ConditionSignal(com_workercondition[a]); Sys_ConditionSignal(com_workercondition[a]);
Sys_UnlockConditional(com_workercondition[a]); Sys_UnlockConditional(com_workercondition[a]);
}*/
static void COM_WorkerSync_WorkerStopped(void *ctx, void *data, size_t a, size_t b)
{
struct com_worker_s *thread = ctx;
if (thread->thread)
{
//the worker signaled us then stopped looping
Sys_WaitOnThread(thread->thread);
thread->thread = NULL;
Sys_LockConditional(com_workercondition[b]);
com_liveworkers[b] -= 1;
Sys_UnlockConditional(com_workercondition[b]);
}
else
Con_Printf("worker thread died twice?\n");
//if that was the last thread, make sure any work pending for that group is completed.
if (!com_liveworkers[b])
{
while(COM_DoWork(b, false))
;
}
} }
static int COM_WorkerThread(void *arg) static int COM_WorkerThread(void *arg)
{ {
int thread = (void**)arg - com_workerthread; struct com_worker_s *thread = arg;
Sys_LockConditional(com_workercondition[thread]); int group = WG_LOADER;
do Sys_LockConditional(com_workercondition[group]);
com_liveworkers[group]++;
for(;;)
{ {
while(COM_DoWork(thread, true)) while(COM_DoWork(group, true))
; ;
if (com_workerdone[thread]) if (thread->request) //flagged from some work
{
if (thread->request == WR_DIE)
break; break;
} while (Sys_ConditionWait(com_workercondition[thread])); if (thread->request == WR_ACK)
Sys_UnlockConditional(com_workercondition[thread]); {
thread->request = WR_NONE;
thread->ackseq = com_workeracksequence;
Sys_UnlockConditional(com_workercondition[group]);
Sys_ConditionBroadcast(com_workercondition[WG_MAIN]); //try to wake up whoever wanted us to ack them
Sys_LockConditional(com_workercondition[group]);
continue;
}
}
else if (!Sys_ConditionWait(com_workercondition[group]))
break;
}
Sys_UnlockConditional(com_workercondition[group]);
//no more work please... //and wake up main thread to clean up our handle
*(void**)arg = NULL; COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, thread, NULL, 0, group);
//and wake up main thread
COM_WorkerSync_SignalMain(NULL, NULL, 0, 0);
return 0; return 0;
} }
static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b)
{
//posted to main thread from a worker.
Sys_Error(data);
}
void COM_WorkerAbort(char *message)
{
int group = -1;
int us;
if (Sys_IsMainThread())
return;
com_workererror = true;
if (!com_workercondition[WG_MAIN])
return; //Sys_IsMainThread was probably called too early...
//find out which worker we are, and tell the main thread to clean us up
for (us = 0; us < WORKERTHREADS; us++)
if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))
{
group = WG_LOADER;
COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, &com_worker[us], NULL, 0, group);
break;
}
//now tell the main thread that it should be crashing, and why.
COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);
Sys_ThreadAbort();
}
#ifndef COM_AssertMainThread #ifndef COM_AssertMainThread
void COM_AssertMainThread(const char *msg) void COM_AssertMainThread(const char *msg)
@ -4936,48 +4972,29 @@ void COM_AssertMainThread(const char *msg)
void COM_DestroyWorkerThread(void) void COM_DestroyWorkerThread(void)
{ {
int i; int i;
COM_WorkerFullSync(); if (!com_resourcemutex)
return;
// com_workererror = false; // com_workererror = false;
Sys_LockConditional(com_workercondition[WG_LOADER]);
for (i = 0; i < WORKERTHREADS; i++) for (i = 0; i < WORKERTHREADS; i++)
{ com_worker[i].request = WR_DIE; //flag them all to die
if (com_workerthread[i]) Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up
{ Sys_UnlockConditional(com_workercondition[WG_LOADER]);
void *thread = com_workerthread[i];
com_workerdone[0] = false;
//send it the terminate message
COM_AddWork(i, COM_WorkerSync_StopWorker, NULL, NULL, i, 0);
//wait for the response while servicing anything that it might be waiting for. while(COM_DoWork(WG_LOADER, false)) //finish any work that got posted to it that it neglected to finish.
Sys_LockConditional(com_workercondition[0]);
do
{
if (com_workererror)
break;
while(COM_DoWork(0, true))
; ;
if (com_workerdone[0]) while(COM_DoWork(WG_MAIN, false))
break;
} while (Sys_ConditionWait(com_workercondition[0]));
Sys_UnlockConditional(com_workercondition[0]);
//and now that we know its going down and will not wait any more, we can block for its final moments
Sys_WaitOnThread(thread);
//finish any work that got posted to it that it neglected to finish.
while(COM_DoWork(i, true))
; ;
}
}
for (i = 0; i < WORKERTHREADS; i++) COM_WorkerFullSync();
for (i = 0; i < WG_COUNT; i++)
{ {
if (com_workercondition[i]) if (com_workercondition[i])
Sys_DestroyConditional(com_workercondition[i]); Sys_DestroyConditional(com_workercondition[i]);
com_workercondition[i] = NULL; com_workercondition[i] = NULL;
com_workerthread[i] = NULL;
} }
if (com_resourcemutex)
Sys_DestroyMutex(com_resourcemutex); Sys_DestroyMutex(com_resourcemutex);
com_resourcemutex = NULL; com_resourcemutex = NULL;
} }
@ -4988,37 +5005,57 @@ void COM_WorkerFullSync(void)
qboolean repeat; qboolean repeat;
int i; int i;
for (i = 1; i < WORKERTHREADS; i++) while(COM_DoWork(WG_MAIN, false))
{ ;
if (!com_workerthread[i])
continue;
//main thread asks worker thread to set main thread's 'done' flag. if (!com_liveworkers[WG_LOADER])
//the worker might be posting work to the main thread and back (shaders with texures) so make sure that the only work we do before the reply is the reply itself. return;
com_workeracksequence++;
Sys_LockConditional(com_workercondition[WG_MAIN]);
do do
{ {
int cmds = 0; if (!COM_HasWork())
com_workerdone[0] = false;
repeat = COM_HasWork();
COM_AddWork(i, COM_WorkerSync_SignalMain, NULL, NULL, 0, 0);
Sys_LockConditional(com_workercondition[0]);
do
{ {
if (com_workererror) Sys_UnlockConditional(com_workercondition[WG_MAIN]);
break; Sys_LockConditional(com_workercondition[WG_LOADER]);
while(COM_DoWork(0, true)) repeat = false;
cmds++; for (i = 0; i < WORKERTHREADS; i++)
if (com_workerdone[0]) {
break; if (com_worker[i].ackseq != com_workeracksequence && com_worker[i].request == WR_NONE)
} while (Sys_ConditionWait(com_workercondition[0])); {
Sys_UnlockConditional(com_workercondition[0]); com_worker[i].request = WR_ACK;
if (com_workererror)
break;
if (cmds > 1)
repeat = true; repeat = true;
} while (COM_DoWork(0, false) || repeat); //outer loop ensures there isn't anything pingponging between
} }
} }
if (repeat) //we're unable to signal a specific thread due to only having one condition. oh well. WAKE UP GUYS!
Sys_ConditionBroadcast(com_workercondition[WG_LOADER]);
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
Sys_LockConditional(com_workercondition[WG_MAIN]);
}
repeat = COM_DoWork(WG_MAIN, true);
if (repeat)
{ //if we just did something, we may have posted something new to a worker... bum.
com_workeracksequence++;
}
else
{
for (i = 0; i < WORKERTHREADS; i++)
{
if (com_worker[i].thread && com_worker[i].ackseq != com_workeracksequence)
repeat = true;
}
if (repeat)
Sys_ConditionWait(com_workercondition[WG_MAIN]);
}
if (com_workererror)
break;
} while(repeat);
Sys_UnlockConditional(com_workercondition[WG_MAIN]);
}
//main thread wants a specific object to be prioritised. //main thread wants a specific object to be prioritised.
//an ancestor of the work must be pending on either the main thread or the worker thread. //an ancestor of the work must be pending on either the main thread or the worker thread.
@ -5032,18 +5069,20 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
// Con_Printf("waiting for %p %s\n", priorityctx, priorityctx); // Con_Printf("waiting for %p %s\n", priorityctx, priorityctx);
COM_DoWork(WG_MAIN, false);
//boost the priority of the object that we're waiting for on the other thread, if we can find it. //boost the priority of the object that we're waiting for on the other thread, if we can find it.
//this avoids waiting for everything. //this avoids waiting for everything.
//if we can't find it, then its probably currently being processed anyway. //if we can't find it, then its probably currently being processed anyway.
//main thread is meant to do all loadstate value changes anyway, ensuring that we're woken up properly in this case. //main thread is meant to do all loadstate value changes anyway, ensuring that we're woken up properly in this case.
if (priorityctx) if (priorityctx)
{ {
unsigned int thread; unsigned int grp;
qboolean found = false; qboolean found = false;
for (thread = 1; thread < WORKERTHREADS && !found; thread++) for (grp = WG_LOADER; grp < WG_MAIN && !found; grp++)
{ {
Sys_LockConditional(com_workercondition[thread]); Sys_LockConditional(com_workercondition[grp]);
for (link = &com_work_head[thread], work = NULL; *link; link = &(*link)->next) for (link = &com_work_head[grp], work = NULL; *link; link = &(*link)->next)
{ {
prev = work; prev = work;
work = *link; work = *link;
@ -5052,30 +5091,30 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
*link = work->next; *link = work->next;
if (!work->next) if (!work->next)
com_work_tail[thread] = prev; com_work_tail[grp] = prev;
//link it in at the head, so its the next thing seen. //link it in at the head, so its the next thing seen.
work->next = com_work_head[thread]; work->next = com_work_head[grp];
com_work_head[thread] = work; com_work_head[grp] = work;
if (!work->next) if (!work->next)
com_work_tail[thread] = work; com_work_tail[grp] = work;
found = true; found = true;
break; //found it, nothing else to do. break; //found it, nothing else to do.
} }
} }
//we've not actually added any work, so no need to signal //we've not actually added any work, so no need to signal
Sys_UnlockConditional(com_workercondition[thread]); Sys_UnlockConditional(com_workercondition[grp]);
} }
if (!found) if (!found)
Con_DPrintf("Might be in for a long wait for %s\n", (char*)priorityctx); Con_DPrintf("Might be in for a long wait for %s\n", (char*)priorityctx);
} }
Sys_LockConditional(com_workercondition[0]); Sys_LockConditional(com_workercondition[WG_MAIN]);
do do
{ {
if (com_workererror) if (com_workererror)
break; break;
while(COM_DoWork(0, true)) while(COM_DoWork(WG_MAIN, true))
{ {
//give up as soon as we're done //give up as soon as we're done
if (*address != value) if (*address != value)
@ -5084,8 +5123,8 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
//if our object's state has changed, we're done //if our object's state has changed, we're done
if (*address != value) if (*address != value)
break; break;
} while (Sys_ConditionWait(com_workercondition[0])); } while (Sys_ConditionWait(com_workercondition[WG_MAIN]));
Sys_UnlockConditional(com_workercondition[0]); Sys_UnlockConditional(com_workercondition[WG_MAIN]);
// Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx); // Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx);
} }
@ -5094,43 +5133,74 @@ static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)
{ {
double *timestamp = data; double *timestamp = data;
if (!b) if (!b)
COM_AddWork(0, COM_WorkerPing, ctx, data, 0, 1); COM_AddWork(WG_MAIN, COM_WorkerPing, ctx, data, 0, 1);
else else
{ {
Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp); Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp);
} }
} }
static void COM_WorkerTest_f(void) static void COM_WorkerTest_f(void)
{
if (com_workerthread)
{ {
double *timestamp = Z_Malloc(sizeof(*timestamp)); double *timestamp = Z_Malloc(sizeof(*timestamp));
*timestamp = Sys_DoubleTime(); *timestamp = Sys_DoubleTime();
COM_AddWork(1, COM_WorkerPing, NULL, timestamp, 0, 0); COM_AddWork(WG_LOADER, COM_WorkerPing, NULL, timestamp, 0, 0);
}
static void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue)
{
int i, count = var->ival;
if (!*var->string)
{
count = 4;
}
//try to respond to any kill requests now, so we don't get surprised by the cvar changing too often.
while(COM_DoWork(WG_MAIN, false))
;
for (i = 0; i < WORKERTHREADS; i++)
{
if (i >= count)
{
//higher thread indexes need to die.
com_worker[i].request = WR_DIE; //flag them all to die
} }
else else
Con_Printf("Worker is not active.\n"); {
//lower thread indexes need to be created
if (!com_worker[i].thread)
{
com_worker[i].request = WR_NONE;
com_worker[i].thread = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_worker[i], 0, 256*1024);
} }
}
}
cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling."); Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up to check their new death values.
}
cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling the main thread.");
cvar_t worker_count = CVARFDC("worker_count", "", CVAR_NOTFROMSERVER, "Specifies the number of worker threads to utilise.", COM_WorkerCount_Change);
static void COM_InitWorkerThread(void) static void COM_InitWorkerThread(void)
{ {
int i; int i;
//in theory, we could run multiple workers, signalling a different one in turn for each bit of work. //in theory, we could run multiple workers, signalling a different one in turn for each bit of work.
com_resourcemutex = Sys_CreateMutex(); com_resourcemutex = Sys_CreateMutex();
for (i = 0; i < WORKERTHREADS; i++) for (i = 0; i < WG_COUNT; i++)
{ {
com_workercondition[i] = Sys_CreateConditional(); com_workercondition[i] = Sys_CreateConditional();
} }
if (!COM_CheckParm("-noworker")) com_liveworkers[WG_MAIN] = 1;
//technically its ready now...
if (COM_CheckParm("-noworker"))
{ {
for (i = 1; i < WORKERTHREADS; i++) worker_count.string = "0";
{ worker_count.flags |= CVAR_NOSET;
com_workerthread[i] = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_workerthread[i], 0, 256*1024);
}
} }
Cvar_Register(&worker_count, NULL);
Cvar_ForceCallback(&worker_count);
Cmd_AddCommand ("worker_test", COM_WorkerTest_f); Cmd_AddCommand ("worker_test", COM_WorkerTest_f);
Cvar_Register(&worker_flush, NULL); Cvar_Register(&worker_flush, NULL);

View file

@ -429,10 +429,16 @@ typedef struct {
} flocation_t; } flocation_t;
struct vfsfile_s; struct vfsfile_s;
typedef enum {FSLFRT_IFFOUND, FSLFRT_LENGTH, FSLFRT_DEPTH_OSONLY, FSLFRT_DEPTH_ANYPATH} FSLF_ReturnType_e; #define FSLF_IFFOUND 0 //returns true (found) / false (not found)
#define FSLF_DEPTH_EXPLICIT 1 //retrieves relative depth (ie: lower = higher priority) for determining which gamedir a file was from
#define FSLF_DEPTH_INEXPLICIT 2 //depth is incremented for EVERY package, not just system/explicit paths.
#define FSLF_SECUREONLY (1u<<4) //ignore files from downloaded packages (ie: configs)
#define FSLF_DONTREFERENCE (1u<<5) //don't add any reference flags to packages
#define FSLF_IGNOREPURE (1u<<6) //use only the client's package list, ignore any lists obtained from the server (including any reordering)
#define FSLF_IGNORELINKS (1u<<7) //ignore any pak/pk3 symlinks. system ones may still be followed.
//if loc is valid, loc->search is always filled in, the others are filled on success. //if loc is valid, loc->search is always filled in, the others are filled on success.
//returns -1 if couldn't find. int FS_FLocateFile(const char *filename, unsigned int flags, flocation_t *loc);
int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation_t *loc);
struct vfsfile_s *FS_OpenReadLocation(flocation_t *location); struct vfsfile_s *FS_OpenReadLocation(flocation_t *location);
char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced); char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced);
@ -442,8 +448,8 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean
qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); //returns false if the name is invalid. qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); //returns false if the name is invalid.
void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags); void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags);
#define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,ignorepacks?FSLFRT_DEPTH_OSONLY:FSLFRT_DEPTH_ANYPATH, NULL) #define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,FSLF_DONTREFERENCE|(ignorepacks?FSLF_DEPTH_EXPLICIT:FSLF_DEPTH_INEXPLICIT), NULL)
#define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLFRT_IFFOUND, NULL) #define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLF_IFFOUND, NULL)
typedef struct vfsfile_s typedef struct vfsfile_s
{ {

View file

@ -620,10 +620,11 @@ static unsigned int fs_pureseed; //used as a key so the server knows we're obeyi
int QDECL COM_FileSize(const char *path) int QDECL COM_FileSize(const char *path)
{ {
int len;
flocation_t loc; flocation_t loc;
len = FS_FLocateFile(path, FSLFRT_LENGTH, &loc); if (FS_FLocateFile(path, FSLF_IFFOUND, &loc))
return len; return loc.len;
else
return -1;
} }
//appends a / on the end of the directory if it does not already have one. //appends a / on the end of the directory if it does not already have one.
@ -744,7 +745,7 @@ void COM_Locate_f (void)
char *f = Cmd_Argv(1); char *f = Cmd_Argv(1);
if (strchr(f, '^')) //fte's filesystem is assumed to be utf-8, but that doesn't mean that console input is. and I'm too lazy to utf-8ify the string (in part because markup can be used to exploit ascii assumptions). if (strchr(f, '^')) //fte's filesystem is assumed to be utf-8, but that doesn't mean that console input is. and I'm too lazy to utf-8ify the string (in part because markup can be used to exploit ascii assumptions).
Con_Printf("Warning: filename contains markup. If this is because of unicode, set com_parseutf8 1\n"); Con_Printf("Warning: filename contains markup. If this is because of unicode, set com_parseutf8 1\n");
if (FS_FLocateFile(f, FSLFRT_LENGTH, &loc)>=0) if (FS_FLocateFile(f, FSLF_IFFOUND, &loc))
{ {
if (!*loc.rawname) if (!*loc.rawname)
{ {
@ -1000,7 +1001,7 @@ Sets com_filesize and one of handle or file
*/ */
//if loc is valid, loc->search is always filled in, the others are filled on success. //if loc is valid, loc->search is always filled in, the others are filled on success.
//returns -1 if couldn't find. //returns -1 if couldn't find.
int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation_t *loc) int FS_FLocateFile(const char *filename, unsigned int lflags, flocation_t *loc)
{ {
int depth=0; int depth=0;
searchpath_t *search; searchpath_t *search;
@ -1026,7 +1027,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
goto fail; goto fail;
} }
if (com_fs_cache.ival && !com_fschanged) if (com_fs_cache.ival && !com_fschanged && !(lflags & FSLF_IGNOREPURE))
{ {
pf = Hash_GetInsensitive(&filesystemhash, filename); pf = Hash_GetInsensitive(&filesystemhash, filename);
if (!pf) if (!pf)
@ -1035,17 +1036,19 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
else else
pf = NULL; pf = NULL;
if (com_purepaths && found == FF_NOTFOUND) if (com_purepaths && found == FF_NOTFOUND && !(lflags & FSLF_IGNOREPURE))
{ {
//check if its in one of the 'pure' packages. these override the default ones. //check if its in one of the 'pure' packages. these override the default ones.
for (search = com_purepaths ; search ; search = search->nextpure) for (search = com_purepaths ; search ; search = search->nextpure)
{ {
depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); if ((lflags & FSLF_SECUREONLY) && !(search->flags & SPF_UNTRUSTED))
continue;
depth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_EXPLICIT));
fs_finds++; fs_finds++;
found = search->handle->FindFile(search->handle, loc, filename, pf); found = search->handle->FindFile(search->handle, loc, filename, pf);
if (found) if (found)
{ {
if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH) if (!(lflags & FSLF_DONTREFERENCE))
{ {
if ((search->flags & fs_referencetype) != fs_referencetype) if ((search->flags & fs_referencetype) != fs_referencetype)
Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename); Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename);
@ -1057,17 +1060,19 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
} }
} }
if (fs_puremode < 2 && found == FF_NOTFOUND) if (((lflags & FSLF_IGNOREPURE) || fs_puremode < 2) && found == FF_NOTFOUND)
{ {
// optionally check the non-pure paths too. // optionally check the non-pure paths too.
for (search = com_searchpaths ; search ; search = search->next) for (search = com_searchpaths ; search ; search = search->next)
{ {
depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); if ((lflags & FSLF_SECUREONLY) && !(search->flags & SPF_UNTRUSTED))
continue;
depth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_EXPLICIT));
fs_finds++; fs_finds++;
found = search->handle->FindFile(search->handle, loc, filename, pf); found = search->handle->FindFile(search->handle, loc, filename, pf);
if (found) if (found)
{ {
if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH) if (!(lflags & FSLF_DONTREFERENCE))
{ {
if ((search->flags & fs_referencetype) != fs_referencetype) if ((search->flags & fs_referencetype) != fs_referencetype)
Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename); Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename);
@ -1079,7 +1084,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
} }
} }
fail: fail:
if (found == FF_SYMLINK) if (found == FF_SYMLINK && !(lflags & FSLF_IGNORELINKS))
{ {
static int blocklink; static int blocklink;
if (blocklink < 4 && loc->len < MAX_QPATH) if (blocklink < 4 && loc->len < MAX_QPATH)
@ -1125,7 +1130,7 @@ fail:
//and locate that instead. //and locate that instead.
blocklink++; blocklink++;
depth = FS_FLocateFile(mergedname, returntype, loc); depth = FS_FLocateFile(mergedname, lflags, loc);
blocklink--; blocklink--;
if (!loc->search) if (!loc->search)
Con_Printf("Symlink %s -> %s (%s) is dead\n", filename, targname, mergedname); Con_Printf("Symlink %s -> %s (%s) is dead\n", filename, targname, mergedname);
@ -1143,20 +1148,14 @@ fail:
else else
Con_Printf("Failed\n"); Con_Printf("Failed\n");
*/ */
if (returntype == FSLFRT_IFFOUND) if (lflags & (FSLF_DEPTH_EXPLICIT | FSLF_DEPTH_INEXPLICIT))
return (found != FF_NOTFOUND) && (loc->len != -1);
else if (returntype == FSLFRT_LENGTH)
{
if (found == FF_NOTFOUND)
return -1;
return loc->len;
}
else
{ {
if (found == FF_NOTFOUND) if (found == FF_NOTFOUND)
return 0x7fffffff; return 0x7fffffff;
return depth; return depth;
} }
else
return (found != FF_NOTFOUND) && (loc->len != -1);
} }
char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced) char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced)
@ -1704,7 +1703,7 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r
break; break;
} }
FS_FLocateFile(filename, FSLFRT_IFFOUND, &loc); FS_FLocateFile(filename, FSLF_IFFOUND, &loc);
if (loc.search) if (loc.search)
{ {
@ -1856,9 +1855,8 @@ qbyte *COM_LoadFile (const char *path, int usehunk, size_t *filesize)
qbyte *buf; qbyte *buf;
qofs_t len; qofs_t len;
flocation_t loc; flocation_t loc;
FS_FLocateFile(path, FSLFRT_LENGTH, &loc);
if (!loc.search) if (!FS_FLocateFile(path, FSLF_IFFOUND, &loc) || !loc.search)
return NULL; //wasn't found return NULL; //wasn't found
if (loc.len > 0x7fffffff) //don't malloc 5000gb sparse files or anything crazy on a 32bit system... if (loc.len > 0x7fffffff) //don't malloc 5000gb sparse files or anything crazy on a 32bit system...
@ -2834,7 +2832,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name)
if (search) if (search)
found = search->FindFile(search, &loc, name, NULL); found = search->FindFile(search, &loc, name, NULL);
else else
found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); found = FS_FLocateFile(name, FSLF_IFFOUND, &loc);
if (found) if (found)
{ {
f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb");
@ -2855,7 +2853,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name)
if (search) if (search)
found = search->FindFile(search, &loc, name, NULL); found = search->FindFile(search, &loc, name, NULL);
else else
found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); found = FS_FLocateFile(name, FSLF_IFFOUND, &loc);
if (found) if (found)
{ {
f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb");
@ -2939,13 +2937,19 @@ char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum)
int numpaks = 0; int numpaks = 0;
searchpath_t *sp; searchpath_t *sp;
FS_FLocateFile("vm/cgame.qvm", FSLFRT_LENGTH, &loc); if (FS_FLocateFile("vm/cgame.qvm", FSLF_IFFOUND, &loc))
{
Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen);
basechecksum ^= loc.search->crc_reply; basechecksum ^= loc.search->crc_reply;
}
else Q_strncatz(buffer, va("%i ", 0), maxlen);
FS_FLocateFile("vm/ui.qvm", FSLFRT_LENGTH, &loc); if (FS_FLocateFile("vm/ui.qvm", FSLF_IFFOUND, &loc))
{
Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen);
basechecksum ^= loc.search->crc_reply; basechecksum ^= loc.search->crc_reply;
}
else Q_strncatz(buffer, va("%i ", 0), maxlen);
Q_strncatz(buffer, "@ ", maxlen); Q_strncatz(buffer, "@ ", maxlen);
@ -4509,12 +4513,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
for (i = 0; i < countof(vidfile); i++) for (i = 0; i < countof(vidfile); i++)
{ {
FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); //q1 FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); //q1
vidpath[i] = loc.search?loc.search->handle:NULL; vidpath[i] = loc.search?loc.search->handle:NULL;
} }
for (i = 0; i < countof(conffile); i++) for (i = 0; i < countof(conffile); i++)
{ {
FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); //q1 FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); //q1
confpath[i] = loc.search?loc.search->handle:NULL; confpath[i] = loc.search?loc.search->handle:NULL;
} }
@ -4689,7 +4693,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
{ {
for (i = 0; i < countof(vidfile); i++) for (i = 0; i < countof(vidfile); i++)
{ {
FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc);
if (vidpath[i] != (loc.search?loc.search->handle:NULL)) if (vidpath[i] != (loc.search?loc.search->handle:NULL))
{ {
vidrestart = true; vidrestart = true;
@ -4700,7 +4704,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
for (i = 0; i < countof(conffile); i++) for (i = 0; i < countof(conffile); i++)
{ {
FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc);
if (confpath[i] != (loc.search?loc.search->handle:NULL)) if (confpath[i] != (loc.search?loc.search->handle:NULL))
{ {
reloadconfigs = true; reloadconfigs = true;

View file

@ -1365,7 +1365,12 @@ void Plug_Initialise(qboolean fromgamedir)
if (!numplugbuiltins) if (!numplugbuiltins)
{ {
Cvar_Register(&plug_sbar, "plugins"); Cvar_Register(&plug_sbar, "plugins");
#ifdef SUBSERVERS
if (!SSV_IsSubServer())
#endif
Cvar_Register(&plug_loaddefault, "plugins"); Cvar_Register(&plug_loaddefault, "plugins");
Cmd_AddCommand("plug_closeall", Plug_CloseAll_f); Cmd_AddCommand("plug_closeall", Plug_CloseAll_f);
Cmd_AddCommand("plug_close", Plug_Close_f); Cmd_AddCommand("plug_close", Plug_Close_f);
Cmd_AddCommand("plug_load", Plug_Load_f); Cmd_AddCommand("plug_load", Plug_Load_f);

View file

@ -2145,7 +2145,7 @@ void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
qboolean makereferenced = prinst->callargc>1?G_FLOAT(OFS_PARM1):true; qboolean makereferenced = prinst->callargc>1?G_FLOAT(OFS_PARM1):true;
flocation_t loc; flocation_t loc;
if (FS_FLocateFile(srcname, FSLFRT_IFFOUND, &loc)) if (FS_FLocateFile(srcname, FSLF_IFFOUND, &loc))
{ {
srcname = FS_WhichPackForLocation(&loc, makereferenced); srcname = FS_WhichPackForLocation(&loc, makereferenced);
if (srcname == NULL) if (srcname == NULL)

View file

@ -844,7 +844,7 @@ enum clcq2_ops_e
enum { enum {
TE_SPIKE = 0, TE_SPIKE = 0,
TE_SUPERSPIKE = 1, TE_SUPERSPIKE = 1,
TE_GUNSHOT = 2, TE_GUNSHOT = 2, //qw has count byte, nq does not
TE_EXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq. TE_EXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq.
TE_TAREXPLOSION = 4, TE_TAREXPLOSION = 4,
TE_LIGHTNING1 = 5, TE_LIGHTNING1 = 5,
@ -869,6 +869,7 @@ enum {
TEQW_BEAM = 18, //use the builtin, luke. TEQW_BEAM = 18, //use the builtin, luke.
TEQW_EXPLOSION2 = 19, //use the builtin, luke. TEQW_EXPLOSION2 = 19, //use the builtin, luke.
TEQW_EXPLOSIONNOSPRITE = 20, TEQW_EXPLOSIONNOSPRITE = 20,
TE_GUNSHOT_NQCOMPAT = 21, //nq has count byte, qw does not
// hexen 2 // hexen 2
TEH2_STREAM_LIGHTNING_SMALL = 24, TEH2_STREAM_LIGHTNING_SMALL = 24,

View file

@ -23,7 +23,7 @@ int VM_fopen (char *name, int *handle, int fmode, int owner)
size_t insize; size_t insize;
if (!handle) if (!handle)
return FS_FLocateFile(name, FSLFRT_IFFOUND, NULL); return FS_FLocateFile(name, FSLF_IFFOUND, NULL);
*handle = 0; *handle = 0;

View file

@ -354,23 +354,20 @@ qboolean Sys_ConditionWait(void *condv)
#endif #endif
EnterCriticalSection(&cv->countlock); EnterCriticalSection(&cv->countlock);
done = cv->release > 0 && cv->waitgeneration != mygen; done = cv->release > 0 && cv->waitgeneration != mygen;
LeaveCriticalSection(&cv->countlock);
if (done) if (done)
break; {
}
EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition
// update waiting count and alert signaling thread that we're done to avoid the deadlock condition
EnterCriticalSection(&cv->countlock);
cv->waiters--; cv->waiters--;
cv->release--; cv->release--;
done = cv->release == 0; done = cv->release == 0;
LeaveCriticalSection(&cv->countlock);
if (done) if (done)
ResetEvent(cv->evnt); ResetEvent(cv->evnt);
LeaveCriticalSection(&cv->countlock);
break;
}
LeaveCriticalSection(&cv->countlock);
}
EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition
return true; return true;
} }
@ -384,6 +381,7 @@ qboolean Sys_ConditionSignal(void *condv)
EnterCriticalSection(&cv->countlock); EnterCriticalSection(&cv->countlock);
if (cv->waiters > cv->release) if (cv->waiters > cv->release)
{ {
if (!cv->release)
SetEvent(cv->evnt); SetEvent(cv->evnt);
cv->release++; cv->release++;
cv->waitgeneration++; cv->waitgeneration++;
@ -405,6 +403,7 @@ qboolean Sys_ConditionBroadcast(void *condv)
EnterCriticalSection(&cv->countlock); EnterCriticalSection(&cv->countlock);
if (cv->waiters > 0) if (cv->waiters > 0)
{ {
if (!cv->release)
SetEvent(cv->evnt); SetEvent(cv->evnt);
cv->release = cv->waiters; cv->release = cv->waiters;
cv->waitgeneration++; cv->waitgeneration++;

View file

@ -253,6 +253,7 @@ void QDECL World_ReleaseCollisionMesh(wedict_t *ed);
void World_Destroy (world_t *w); void World_Destroy (world_t *w);
void World_RBE_Start(world_t *world); void World_RBE_Start(world_t *world);
void World_RBE_Shutdown(world_t *world);
void World_ClearWorld (world_t *w); void World_ClearWorld (world_t *w);

View file

@ -1264,7 +1264,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)
lightdir[2] = 1; lightdir[2] = 1;
} }
if (!r_vertexdlights.ival && r_dynamic.ival) if (!r_vertexdlights.ival && r_dynamic.ival > 0)
{ {
float *org = e->origin; float *org = e->origin;
if (e->flags & RF_WEAPONMODEL) if (e->flags & RF_WEAPONMODEL)

View file

@ -4870,7 +4870,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist)
// r_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]); // r_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]);
r_refdef.recurse+=1; //paranoid, should stop potential infinite loops r_refdef.recurse+=1; //paranoid, should stop potential infinite loops
GLBE_SubmitMeshes(true, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE);
r_refdef.recurse-=1; r_refdef.recurse-=1;
GLBE_FBO_Pop(oldfbo); GLBE_FBO_Pop(oldfbo);
@ -4886,25 +4886,24 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist)
} }
} }
void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop) void GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop)
{ {
model_t *model = cl.worldmodel;
int i; int i;
int portaldepth = r_portalrecursion.ival; int portaldepth = r_portalrecursion.ival;
for (i = start; i <= stop; i++) for (i = start; i <= stop; i++)
{ {
if (drawworld) if (worldbatches)
{ {
if (i == SHADER_SORT_PORTAL && r_refdef.recurse < portaldepth) if (i == SHADER_SORT_PORTAL && r_refdef.recurse < portaldepth)
{ {
GLBE_SubmitMeshesPortals(model->batches, shaderstate.mbatches[i]); GLBE_SubmitMeshesPortals(worldbatches, shaderstate.mbatches[i]);
if (!r_refdef.recurse && r_portalonly.ival) if (!r_refdef.recurse && r_portalonly.ival)
return; return;
} }
GLBE_SubmitMeshesSortList(model->batches[i]); GLBE_SubmitMeshesSortList(worldbatches[i]);
} }
GLBE_SubmitMeshesSortList(shaderstate.mbatches[i]); GLBE_SubmitMeshesSortList(shaderstate.mbatches[i]);
} }
@ -4941,6 +4940,8 @@ static void BE_UpdateLightmaps(void)
continue; continue;
if (lm->modified) if (lm->modified)
{ {
int t = lm->rectchange.t; //pull them out now, in the hopes that it'll be more robust with respect to r_dynamic -1
int b = lm->rectchange.b;
lm->modified = false; lm->modified = false;
if (!TEXVALID(lm->lightmap_texture)) if (!TEXVALID(lm->lightmap_texture))
{ {
@ -4957,14 +4958,14 @@ static void BE_UpdateLightmaps(void)
else else
{ {
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture); GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, lm->rectchange.t, qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t,
lm->width, lm->rectchange.h, glformat, gltype, lm->width, b-t, glformat, gltype,
lm->lightmaps+(lm->rectchange.t) *lm->width*lightmap_bytes); lm->lightmaps+t *lm->width*lightmap_bytes);
} }
lm->rectchange.l = lm->width; lm->rectchange.l = lm->width;
lm->rectchange.t = lm->height; lm->rectchange.t = lm->height;
lm->rectchange.h = 0; lm->rectchange.r = 0;
lm->rectchange.w = 0; lm->rectchange.b = 0;
} }
} }
} }
@ -4990,7 +4991,7 @@ void GLBE_BaseEntTextures(void)
batch_t **ob = shaderstate.mbatches; batch_t **ob = shaderstate.mbatches;
shaderstate.mbatches = batches; shaderstate.mbatches = batches;
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode); BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode);
GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
GLBE_SelectEntity(&r_worldentity); GLBE_SelectEntity(&r_worldentity);
shaderstate.mbatches = ob; shaderstate.mbatches = ob;
} }
@ -5340,7 +5341,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
} }
/*do portals*/ /*do portals*/
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL);
BE_SelectMode(BEM_DEPTHNORM); BE_SelectMode(BEM_DEPTHNORM);
if (!shaderstate.depthnormshader) if (!shaderstate.depthnormshader)
@ -5393,7 +5394,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
} }
/*draw surfaces that can be drawn this way*/ /*draw surfaces that can be drawn this way*/
GLBE_SubmitMeshes(true, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE);
/*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/ /*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/
GLBE_FBO_Sources(shaderstate.tex_normals, r_nulltex); GLBE_FBO_Sources(shaderstate.tex_normals, r_nulltex);
@ -5405,7 +5406,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
GLBE_SelectEntity(&r_worldentity); GLBE_SelectEntity(&r_worldentity);
/*now draw the prelights*/ /*now draw the prelights*/
GLBE_SubmitMeshes(true, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT);
/*final reconfigure - now drawing final surface data onto true framebuffer*/ /*final reconfigure - now drawing final surface data onto true framebuffer*/
GLBE_FBO_Pop(oldfbo); GLBE_FBO_Pop(oldfbo);
@ -5414,7 +5415,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
/*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/ /*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/
GLBE_SelectEntity(&r_worldentity); GLBE_SelectEntity(&r_worldentity);
GLBE_SubmitMeshes(true, SHADER_SORT_SKY, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_SKY, SHADER_SORT_NEAREST);
#ifdef RTLIGHTS #ifdef RTLIGHTS
/*regular lighting now*/ /*regular lighting now*/
@ -5426,7 +5427,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
qglClearColor (1,0,0,1); qglClearColor (1,0,0,1);
} }
void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) void GLBE_DrawWorld (batch_t **worldbatches, qbyte *vis)
{ {
extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps; extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps;
batch_t *batches[SHADER_SORT_COUNT]; batch_t *batches[SHADER_SORT_COUNT];
@ -5479,7 +5480,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
GLBE_SelectEntity(&r_worldentity); GLBE_SelectEntity(&r_worldentity);
BE_UpdateLightmaps(); BE_UpdateLightmaps();
if (drawworld) if (worldbatches)
{ {
if (gl_overbright.modified) if (gl_overbright.modified)
{ {
@ -5493,7 +5494,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
} }
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (drawworld && r_shadow_realtime_world.ival) if (worldbatches && r_shadow_realtime_world.ival)
shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value; shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;
else else
#endif #endif
@ -5517,11 +5518,11 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
RSpeedRemark(); RSpeedRemark();
GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
RSpeedEnd(RSPEED_WORLD); RSpeedEnd(RSPEED_WORLD);
#ifdef RTLIGHTS #ifdef RTLIGHTS
if (drawworld) if (worldbatches)
{ {
RSpeedRemark(); RSpeedRemark();
TRACE(("GLBE_DrawWorld: drawing lights\n")); TRACE(("GLBE_DrawWorld: drawing lights\n"));
@ -5535,7 +5536,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
shaderstate.identitylighting = 1; shaderstate.identitylighting = 1;
GLBE_SubmitMeshes(true, SHADER_SORT_DECAL, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(worldbatches, SHADER_SORT_DECAL, SHADER_SORT_NEAREST);
/* if (r_refdef.gfog_alpha) /* if (r_refdef.gfog_alpha)
{ {
@ -5550,7 +5551,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
{ {
BE_SelectMode(BEM_WIREFRAME); BE_SelectMode(BEM_WIREFRAME);
qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
} }
@ -5559,14 +5560,14 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
} }
else else
{ {
GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
#ifdef GL_LINE //no gles #ifdef GL_LINE //no gles
if (r_wireframe.ival && qglPolygonMode) if (r_wireframe.ival && qglPolygonMode)
{ {
BE_SelectMode(BEM_WIREFRAME); BE_SelectMode(BEM_WIREFRAME);
qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
BE_SelectMode(BEM_STANDARD); BE_SelectMode(BEM_STANDARD);
qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
} }

View file

@ -854,7 +854,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
} }
error = FT_Err_Cannot_Open_Resource; error = FT_Err_Cannot_Open_Resource;
if (FS_FLocateFile(fontfilename, FSLFRT_LENGTH, &loc)>0) if (FS_FLocateFile(fontfilename, FSLF_IFFOUND, &loc))
{ {
if (*loc.rawname && !loc.offset) if (*loc.rawname && !loc.offset)
{ {

View file

@ -512,8 +512,8 @@ static qboolean Terr_InitLightmap(hmsection_t *s, qboolean initialise)
lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->modified = true;
lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0; lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
} }
return s->lightmap>=0; return s->lightmap>=0;
@ -1452,8 +1452,8 @@ static void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s)
lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->modified = true;
lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0; lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
} }
for (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++) for (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)
{ {
@ -2101,7 +2101,7 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc)
if (!Terr_SaveSection(hm, s, x, y, false)) if (!Terr_SaveSection(hm, s, x, y, false))
return false; return false;
return FS_FLocateFile(name, FSLFRT_IFFOUND, loc); return FS_FLocateFile(name, FSLF_IFFOUND, loc);
} }
#endif #endif
@ -2240,7 +2240,7 @@ static qboolean Terr_Collect(heightmap_t *hm)
#endif #endif
/*purge all sections, but not root /*purge all sections, but not root
lightmaps only are purged whenever the client rudely kills lightmaps lightmaps only are purged whenever the client rudely kills lightmaps (purges all lightmaps on map changes, to cope with models/maps potentially being unloaded)
we'll reload those when its next seen. we'll reload those when its next seen.
(lightmaps will already have been destroyed, so no poking them) (lightmaps will already have been destroyed, so no poking them)
*/ */
@ -2312,6 +2312,7 @@ void Terr_PurgeTerrainModel(model_t *mod, qboolean lightmapsonly, qboolean light
// Con_Printf("PostPurge: %i lm chunks used, %i unused\n", hm->numusedlmsects, hm->numunusedlmsects); // Con_Printf("PostPurge: %i lm chunks used, %i unused\n", hm->numusedlmsects, hm->numunusedlmsects);
} }
void Terr_FreeModel(model_t *mod) void Terr_FreeModel(model_t *mod)
{ {
heightmap_t *hm = mod->terrain; heightmap_t *hm = mod->terrain;
@ -2354,6 +2355,7 @@ void Terr_FreeModel(model_t *mod)
mod->terrain = NULL; mod->terrain = NULL;
} }
} }
#ifndef SERVERONLY #ifndef SERVERONLY
void Terr_DrawTerrainWater(heightmap_t *hm, float *mins, float *maxs, struct hmwater_s *w) void Terr_DrawTerrainWater(heightmap_t *hm, float *mins, float *maxs, struct hmwater_s *w)
{ {
@ -3831,7 +3833,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
if (!s || s->loadstate != TSLS_LOADED) if (!s || s->loadstate != TSLS_LOADED)
{ {
if ((tr->contents & tr->hm->exteriorcontents) || s->loadstate != TSLS_FAILED) if ((tr->hitcontentsmask & tr->hm->exteriorcontents) || s->loadstate != TSLS_FAILED)
{ {
//you're not allowed to walk into sections that have not loaded. //you're not allowed to walk into sections that have not loaded.
//might as well check the entire section instead of just one tile //might as well check the entire section instead of just one tile
@ -4101,6 +4103,7 @@ qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec
hmtrace.htilesize = hmtrace.hm->sectionsize / (SECTHEIGHTSIZE-1); hmtrace.htilesize = hmtrace.hm->sectionsize / (SECTHEIGHTSIZE-1);
hmtrace.frac = 1; hmtrace.frac = 1;
hmtrace.contents = 0; hmtrace.contents = 0;
hmtrace.hitcontentsmask = against;
hmtrace.plane[0] = 0; hmtrace.plane[0] = 0;
hmtrace.plane[1] = 0; hmtrace.plane[1] = 0;
@ -4389,8 +4392,8 @@ static unsigned char *ted_getlightmap(hmsection_t *s, int idx)
lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->modified = true;
lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0; lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
lm = lightmap[s->lightmap]->lightmaps; lm = lightmap[s->lightmap]->lightmaps;
lm += ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap_bytes; lm += ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap_bytes;
return lm; return lm;
@ -4449,8 +4452,8 @@ static void ted_dorelight(heightmap_t *hm)
lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->modified = true;
lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0; lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
} }
static void ted_sethole(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) static void ted_sethole(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)
{ {
@ -5606,8 +5609,8 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
lm->modified = true; lm->modified = true;
lm->rectchange.l = 0; lm->rectchange.l = 0;
lm->rectchange.t = 0; lm->rectchange.t = 0;
lm->rectchange.w = lm->width; lm->rectchange.r = lm->width;
lm->rectchange.h = lm->height; lm->rectchange.b = lm->height;
in = br->faces[j].lightdata; in = br->faces[j].lightdata;
out = lm->lightmaps + (br->faces[j].lmbase[1] * lm->width + br->faces[j].lmbase[0]) * lightmap_bytes; out = lm->lightmaps + (br->faces[j].lmbase[1] * lm->width + br->faces[j].lmbase[0]) * lightmap_bytes;
@ -6159,6 +6162,21 @@ void CL_Parse_BrushEdit(void)
brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes); brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes);
if (!Brush_Deserialise(hm, &brush)) if (!Brush_Deserialise(hm, &brush))
Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n"); Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n");
if (brush.id)
{
int i;
if (cls.demoplayback)
Terr_Brush_DeleteId(hm, brush.id);
else
{
for (i = 0; i < hm->numbrushes; i++)
{
brushes_t *br = &hm->wbrushes[i];
if (br->id == brush.id)
return; //we already have it. assume we just edited it.
}
}
}
Terr_Brush_Insert(mod, hm, &brush); Terr_Brush_Insert(mod, hm, &brush);
} }
else else
@ -6608,7 +6626,7 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s
} }
dist = DotProduct (best, in_normals[j]); dist = DotProduct (best, in_normals[j]);
dist = in_distances[j] - dist; dist = in_distances[j] - dist;
if (dist < 0) if (dist <= 0) //don't find coplanar brushes. add an epsilon if you need this.
break; break;
} }
if (j == in_numplanes) if (j == in_numplanes)
@ -6659,6 +6677,10 @@ void Terr_WriteMapFile(vfsfile_t *file, model_t *mod)
unsigned int entnum = 0; unsigned int entnum = 0;
heightmap_t *hm; heightmap_t *hm;
hm = mod->terrain;
if (hm && hm->exteriorcontents != FTECONTENTS_EMPTY)
VFS_WRITE(file, "terrain\n", 8);
start = entities; start = entities;
while(entities) while(entities)
{ {
@ -7278,13 +7300,36 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force)
return hm; return hm;
} }
#ifndef SERVERONLY
void Mod_Terrain_Create_f(void) void Mod_Terrain_Create_f(void)
{ {
int x,y;
hmsection_t *s;
heightmap_t *hm;
char *mname; char *mname;
char *mdata; char *mapdesc;
char *skyname;
char *groundname;
char *watername;
char *groundheight;
char *waterheight;
vfsfile_t *file;
model_t mod;
memset(&mod, 0, sizeof(mod));
if (Cmd_Argc() < 2)
{
Con_Printf("%s: NAME \"DESCRIPTION\" SKYNAME DEFAULTGROUNDTEX DEFAULTHEIGHT DEFAULTWATER DEFAULTWATERHEIGHT\nGenerates a fresh maps/foo.hmp file. You may wish to edit it with notepad later to customise it. You will need csaddon.dat in order to edit the actual terrain.\n", Cmd_Argv(0));
return;
}
mname = va("maps/%s.hmp", Cmd_Argv(1)); mname = va("maps/%s.hmp", Cmd_Argv(1));
mdata = va(
"terrain\n" mapdesc = Cmd_Argv(2); if (!*mapdesc) mapdesc = Cmd_Argv(1);
skyname = Cmd_Argv(3); if (!*skyname) skyname = "sky1";
groundname = Cmd_Argv(4); if (!*groundname) groundname = "default";
groundheight = Cmd_Argv(5); if (!*groundheight) groundheight = "0";
watername = Cmd_Argv(6); if (!*watername) watername = "";
waterheight = Cmd_Argv(7); if (!*waterheight) waterheight = "1024";
mod.entities = va(
"{\n" "{\n"
"classname \"worldspawn\"\n" "classname \"worldspawn\"\n"
"message \"%s\"\n" "message \"%s\"\n"
@ -7295,8 +7340,8 @@ void Mod_Terrain_Create_f(void)
"_minysegment -2048\n" "_minysegment -2048\n"
"_maxxsegment 2048\n" "_maxxsegment 2048\n"
"_maxysegment 2048\n" "_maxysegment 2048\n"
"//_defaultgroundtexture city4_2\n" "//_defaultgroundtexture \"city4_2\"\n"
"//_defaultwatertexture *water2\n" "//_defaultwatertexture \"*water2\"\n"
"//_defaultgroundheight -1024\n" "//_defaultgroundheight -1024\n"
"//_defaultwaterheight 0\n" //hurrah, sea level. "//_defaultwaterheight 0\n" //hurrah, sea level.
// "_tiles 64 64 8 8\n" // "_tiles 64 64 8 8\n"
@ -7306,10 +7351,51 @@ void Mod_Terrain_Create_f(void)
"origin \"0 0 1024\"\n" "origin \"0 0 1024\"\n"
"}\n" "}\n"
, Cmd_Argv(2)); , Cmd_Argv(2));
COM_WriteFile(mname, FS_GAMEONLY, mdata, strlen(mdata));
//FIXME: create 4 sections around the origin mod.type = mod_heightmap;
mod.terrain = hm = Z_Malloc(sizeof(*hm));
Terr_ParseEntityLump(mod.entities, hm);
hm->entitylock = Sys_CreateMutex();
ClearLink(&hm->recycle);
Q_strncpyz(hm->path, Cmd_Argv(1), sizeof(hm->path));
Q_strncpyz(hm->groundshadername, "terrainshader", sizeof(hm->groundshadername));
hm->exteriorcontents = FTECONTENTS_SOLID;
for (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++)
for (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++)
Terr_GetSection(hm, x, y, TGS_TRYLOAD|TGS_DEFAULTONFAIL);
for (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++)
for (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++)
{
s = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL);
if (s && (s->flags & (TSF_EDITED|TSF_DIRTY)))
{
Terr_InitLightmap(s, false);
Terr_SaveSection(hm, s, x, y, true);
} }
}
if (COM_FCheckExists(mname))
{
Con_Printf("%s: already exists, not overwriting.\n", mname);
return;
}
FS_CreatePath(mname, FS_GAMEONLY);
file = FS_OpenVFS(mname, "wb", FS_GAMEONLY);
if (!file)
Con_Printf("unable to open %s\n", mname);
else
{
Terr_WriteMapFile(file, &mod);
VFS_CLOSE(file);
Con_Printf("Wrote %s\n", mname);
FS_FlushFSHashWritten();
}
Terr_FreeModel(&mod);
}
#endif
//reads in the terrain a tile at a time, and writes it out again. //reads in the terrain a tile at a time, and writes it out again.
//the new version will match our current format version. //the new version will match our current format version.
//this is mostly so I can strip out old format revisions... //this is mostly so I can strip out old format revisions...
@ -7426,9 +7512,9 @@ void Terr_Init(void)
Cvar_Register(&mod_terrain_defaulttexture, "Terrain"); Cvar_Register(&mod_terrain_defaulttexture, "Terrain");
Cvar_Register(&mod_terrain_savever, "Terrain"); Cvar_Register(&mod_terrain_savever, "Terrain");
Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f); Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f);
Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f);
Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f); Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f);
#ifndef SERVERONLY #ifndef SERVERONLY
Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f);
Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation."); Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation.");
#endif #endif

View file

@ -82,42 +82,16 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)
hlmdl_tex_t *tex; hlmdl_tex_t *tex;
hlmdl_bone_t *bones; hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls; hlmdl_bonecontroller_t *bonectls;
shader_t **shaders; struct hlmodelshaders_s *shaders;
void *texmem = NULL; void *texmem = NULL;
/*~~*/ /*~~*/
//checksum the model
if (mod->engineflags & MDLF_DOCRC)
{
unsigned short crc;
qbyte *p;
int len;
char st[40];
QCRC_Init(&crc);
for (len = com_filesize, p = buffer; len; len--, p++)
QCRC_ProcessByte(&crc, *p);
sprintf(st, "%d", (int) crc);
Info_SetValueForKey (cls.userinfo[0],
(mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name,
st, sizeof(cls.userinfo[0]));
if (cls.state >= ca_connected)
{
CL_SendClientCommand(true, "setinfo %s %d",
(mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name,
(int)crc);
}
}
//load the model into hunk //load the model into hunk
model = ZG_Malloc(&mod->memgroup, sizeof(hlmodelcache_t)); model = ZG_Malloc(&mod->memgroup, sizeof(hlmodelcache_t));
header = ZG_Malloc(&mod->memgroup, com_filesize); header = ZG_Malloc(&mod->memgroup, fsize);
memcpy(header, buffer, com_filesize); memcpy(header, buffer, fsize);
#if defined(HLSERVER) && (defined(__powerpc__) || defined(__ppc__)) #if defined(HLSERVER) && (defined(__powerpc__) || defined(__ppc__))
//this is to let bigfoot know when he comes to port it all... And I'm lazy. //this is to let bigfoot know when he comes to port it all... And I'm lazy.
@ -148,10 +122,12 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)
texheader = NULL; texheader = NULL;
if (!header->numtextures) if (!header->numtextures)
{ {
size_t fz;
char texmodelname[MAX_QPATH]; char texmodelname[MAX_QPATH];
COM_StripExtension(mod->name, texmodelname, sizeof(texmodelname)); COM_StripExtension(mod->name, texmodelname, sizeof(texmodelname));
Q_strncatz(texmodelname, "t.mdl", sizeof(texmodelname));
//no textures? eesh. They must be stored externally. //no textures? eesh. They must be stored externally.
texheader = texmem = (hlmdl_header_t*)FS_LoadMallocFile(va("%st.mdl", texmodelname)); texheader = texmem = (hlmdl_header_t*)FS_LoadMallocFile(texmodelname, &fz);
if (texheader) if (texheader)
{ {
if (texheader->version != 10) if (texheader->version != 10)
@ -184,18 +160,19 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)
memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers);
*/ */
model->header = (char *)header - (char *)model; model->header = header;
model->texheader = (char *)texheader - (char *)model; model->bones = bones;
model->textures = (char *)tex - (char *)model; model->bonectls = bonectls;
model->bones = (char *)bones - (char *)model;
model->bonectls = (char *)bonectls - (char *)model;
shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t)); shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t));
model->shaders = (char *)shaders - (char *)model; model->shaders = shaders;
for(i = 0; i < texheader->numtextures; i++) for(i = 0; i < texheader->numtextures; i++)
{ {
shaders[i] = R_RegisterSkin(va("%s_%i.tga", mod->name, i), mod->name); Q_snprintfz(shaders[i].name, sizeof(shaders[i].name), "%s_%i.tga", mod->name, i);
shaders[i]->defaulttextures.base = R_LoadTexture8Pal24("", tex[i].w, tex[i].h, (qbyte *) texheader + tex[i].offset, (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset, IF_NOALPHA|IF_NOGAMMA); memset(&shaders[i].defaulttex, 0, sizeof(shaders[i].defaulttex));
shaders[i].defaulttex.base = Image_GetTexture(shaders[i].name, "", IF_NOALPHA, (qbyte *) texheader + tex[i].offset, (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset, tex[i].w, tex[i].h, TF_8PAL24);
shaders[i].w = tex[i].w;
shaders[i].h = tex[i].h;
} }
model->numskins = texheader->numtextures; model->numskins = texheader->numtextures;
@ -219,7 +196,7 @@ void *Mod_GetHalfLifeModelData(model_t *mod)
return NULL; //halflife models only, please return NULL; //halflife models only, please
mc = Mod_Extradata(mod); mc = Mod_Extradata(mod);
return (void*)((char*)mc + mc->header); return (void*)mc->header;
} }
#endif #endif
@ -234,7 +211,7 @@ int HLMod_FrameForName(model_t *mod, const char *name)
mc = Mod_Extradata(mod); mc = Mod_Extradata(mod);
h = (hlmdl_header_t *)((char *)mc + mc->header); h = mc->header;
seqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex); seqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex);
for (i = 0; i < h->numseq; i++) for (i = 0; i < h->numseq; i++)
@ -256,7 +233,7 @@ int HLMod_BoneForName(model_t *mod, char *name)
mc = Mod_Extradata(mod); mc = Mod_Extradata(mod);
h = (hlmdl_header_t *)((char *)mc + mc->header); h = mc->header;
bones = (hlmdl_bone_t*)((char*)h+h->boneindex); bones = (hlmdl_bone_t*)((char*)h+h->boneindex);
for (i = 0; i < h->numbones; i++) for (i = 0; i < h->numbones; i++)
@ -278,7 +255,8 @@ int HLMod_BoneForName(model_t *mod, char *name)
void HL_CalculateBones void HL_CalculateBones
( (
int offset, int offset,
int frame, int frame1,
int frame2,
float lerpfrac, float lerpfrac,
vec4_t adjust, vec4_t adjust,
hlmdl_bone_t *bone, hlmdl_bone_t *bone,
@ -290,7 +268,6 @@ void HL_CalculateBones
int i; int i;
vec3_t angle; vec3_t angle;
float lerpifrac = 1-lerpfrac; float lerpifrac = 1-lerpfrac;
float t;
/*~~~~~~~~~~*/ /*~~~~~~~~~~*/
/* For each vector */ /* For each vector */
@ -305,41 +282,30 @@ void HL_CalculateBones
if(animation->offset[o] != 0) if(animation->offset[o] != 0)
{ {
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int tempframe = frame; int tempframe;
hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]); hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]);
short f1, f2;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* find values including the required frame */ /* find values including the required frame */
tempframe = frame1;
while(animvalue->num.total <= tempframe) while(animvalue->num.total <= tempframe)
{ {
tempframe -= animvalue->num.total; tempframe -= animvalue->num.total;
animvalue += animvalue->num.valid + 1; animvalue += animvalue->num.valid + 1;
} }
if(animvalue->num.valid > tempframe) f1 = animvalue[min(animvalue->num.valid-1, tempframe)+1].value;
/* frame2 is always higher than frame1, so keep searching for it, if its in a different block */
tempframe += frame2-frame1;
while(animvalue->num.total <= tempframe)
{ {
if(animvalue->num.valid > (tempframe + 1)) tempframe -= animvalue->num.total;
{ animvalue += animvalue->num.valid + 1;
//we can lerp that
t = animvalue[tempframe + 1].value * lerpifrac + lerpfrac * animvalue[tempframe + 2].value;
}
else
t = animvalue[animvalue->num.valid].value;
angle[i] = bone->value[o] + t * bone->scale[o];
}
else
{
if(animvalue->num.total < tempframe + 1)
{
angle[i] +=
(animvalue[animvalue->num.valid].value * lerpifrac +
lerpfrac * animvalue[animvalue->num.valid + 2].value) *
bone->scale[o];
}
else
{
angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o];
}
} }
f2 = animvalue[min(animvalue->num.valid-1, tempframe)+1].value;
angle[i] += (f1 * lerpifrac + lerpfrac * f2) * bone->scale[o];
} }
if(bone->bonecontroller[o] != -1) if(bone->bonecontroller[o] != -1)
@ -414,32 +380,67 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl
static vec3_t positions[2]; static vec3_t positions[2];
static vec4_t quaternions[2], blended; static vec4_t quaternions[2], blended;
int frame; int frame1, frame2;
hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +
((unsigned int)seqnum>=model->header->numseq?0:seqnum); ((unsigned int)seqnum>=model->header->numseq?0:seqnum);
hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *) hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *)
((qbyte *) model->header + model->header->seqgroups) + ((qbyte *) model->header + model->header->seqgroups) +
sequence->seqindex; sequence->seqindex;
hlmdl_anim_t *animation = (hlmdl_anim_t *) hlmdl_anim_t *animation;
((qbyte *) model->header + sequencedata->data + sequence->index);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
if (sequencedata->name[32])
{
size_t fz;
if (sequence->seqindex >= MAX_ANIM_GROUPS)
{
Sys_Error("Too many animation sequence cache groups\n");
return;
}
if (!model->animcache[sequence->seqindex])
model->animcache[sequence->seqindex] = FS_LoadMallocGroupFile(model->memgroup, sequencedata->name+32, &fz);
if (!model->animcache[sequence->seqindex] || model->animcache[sequence->seqindex]->magic != *(int*)"IDSQ" || model->animcache[sequence->seqindex]->version != 10)
{
Sys_Error("Unable to load %s\n", sequencedata->name+32);
return;
}
animation = (hlmdl_anim_t *)((qbyte*)model->animcache[sequence->seqindex] + sequence->index);
}
else
animation = (hlmdl_anim_t *) ((qbyte *) model->header + sequencedata->data + sequence->index);
frametime *= sequence->timing; frametime *= sequence->timing;
if (frametime < 0) if (frametime < 0)
frametime = 0; frametime = 0;
frame = (int)frametime; frame1 = (int)frametime;
frametime -= frame; frametime -= frame1;
frame2 = frame1+1;
if (!sequence->numframes) if (!sequence->numframes)
return; return;
if(frame >= sequence->numframes) if(frame1 >= sequence->numframes)
{ {
if (sequence->loop) if (sequence->loop)
frame %= sequence->numframes; frame1 %= sequence->numframes;
else else
frame = sequence->numframes-1; frame1 = sequence->numframes-1;
}
if(frame2 >= sequence->numframes)
{
if (sequence->loop)
frame2 %= sequence->numframes;
else
frame2 = sequence->numframes-1;
}
if (frame2 < frame1)
{
i = frame2;
frame2 = frame1;
frame1 = i;
frametime = 1-frametime;
} }
if (lastbone > model->header->numbones) if (lastbone > model->header->numbones)
@ -489,10 +490,10 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl
subblendfrac = 1; subblendfrac = 1;
for(i = firstbone; i < lastbone; i++) for(i = firstbone; i < lastbone; i++)
{ {
HL_CalculateBones(0, frame, frametime, model->adjust, model->bones + i, animation + i, positions[0]); HL_CalculateBones(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[0]);
HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]);
HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i + model->header->numbones, quaternions[1]); HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i + model->header->numbones, quaternions[1]);
QuaternionSlerp(quaternions[0], quaternions[1], subblendfrac, blended); QuaternionSlerp(quaternions[0], quaternions[1], subblendfrac, blended);
QuaternionGLMatrix(blended[0], blended[1], blended[2], blended[3], matrix); QuaternionGLMatrix(blended[0], blended[1], blended[2], blended[3], matrix);
@ -522,8 +523,8 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl
* convert it inside the routine - Inconsistant, but hey.. so's the whole model * convert it inside the routine - Inconsistant, but hey.. so's the whole model
* format. * format.
*/ */
HL_CalculateBones(0, frame, frametime, model->adjust, model->bones + i, animation + i, positions[0]); HL_CalculateBones(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[0]);
HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]);
QuaternionGLMatrix(quaternions[0][0], quaternions[0][1], quaternions[0][2], quaternions[0][3], matrix); QuaternionGLMatrix(quaternions[0][0], quaternions[0][1], quaternions[0][2], quaternions[0][3], matrix);
matrix[0][3] = positions[0][0]; matrix[0][3] = positions[0][0];
@ -548,6 +549,7 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent,
static vecV_t xyz[2048]; static vecV_t xyz[2048];
static vec3_t norm[2048]; static vec3_t norm[2048];
static vec2_t st[2048]; static vec2_t st[2048];
static byte_vec4_t vc[2048];
static index_t index[4096]; static index_t index[4096];
int count; int count;
int b; int b;
@ -568,6 +570,7 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent,
mesh->snormals_array = norm; //for rtlighting mesh->snormals_array = norm; //for rtlighting
mesh->tnormals_array = norm; //for rtlighting mesh->tnormals_array = norm; //for rtlighting
mesh->indexes = index; mesh->indexes = index;
mesh->colors4b_array = vc;
for (b = 0; b < MAX_BONE_CONTROLLERS; b++) for (b = 0; b < MAX_BONE_CONTROLLERS; b++)
model->controller[b] = curent->framestate.bonecontrols[b]; model->controller[b] = curent->framestate.bonecontrols[b];
@ -646,8 +649,14 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent,
st[vert][0] = order[2] * tex_s; st[vert][0] = order[2] * tex_s;
st[vert][1] = order[3] * tex_t; st[vert][1] = order[3] * tex_t;
norm[vert][0] = 1;
norm[vert][1] = 1; norm[vert][1] = 1;
norm[vert][2] = 1;
vc[vert][0] = 255;
vc[vert][1] = 255;
vc[vert][2] = 255;
vc[vert][3] = 255;
order += 4; order += 4;
vert++; vert++;
@ -673,12 +682,12 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches)
static mesh_t bmesh, *mptr = &bmesh; static mesh_t bmesh, *mptr = &bmesh;
//general model //general model
model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header); model.header = modelc->header;
// model.texheader = (hlmdl_header_t *) ((char *)modelc + modelc->texheader); model.bones = modelc->bones;
model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures); model.bonectls = modelc->bonectls;
model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones); model.shaders = modelc->shaders;
model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls); model.animcache = modelc->animcache;
model.shaders = (shader_t **) ((char *)modelc + modelc->shaders); model.memgroup = &rent->model->memgroup;
for (body = 0; body < model.header->numbodyparts; body++) for (body = 0; body < model.header->numbodyparts; body++)
{ {
@ -696,34 +705,40 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches)
hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m; hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m;
float tex_w; float tex_w;
float tex_h; float tex_h;
struct hlmodelshaders_s *s;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
if (mesh->skinindex >= modelc->numskins) if (mesh->skinindex >= modelc->numskins)
continue; continue;
s = &model.shaders[modelc->skins[mesh->skinindex]];
if (batches) if (batches)
{ {
shader_t *shader; int sort, j;
int sort;
b = BE_GetTempBatch(); b = BE_GetTempBatch();
if (!b) if (!b)
return; return;
shader = model.shaders[modelc->skins[mesh->skinindex]]; if (!s->shader)
{
s->shader = R_RegisterSkin(s->name, rent->model->name);
R_BuildDefaultTexnums(&s->defaulttex, s->shader);
}
b->buildmeshes = R_HL_BuildMesh; b->buildmeshes = R_HL_BuildMesh;
b->ent = rent; b->ent = rent;
b->mesh = NULL; b->mesh = NULL;
b->firstmesh = 0; b->firstmesh = 0;
b->meshes = 1; b->meshes = 1;
b->skin = &shader->defaulttextures; b->skin = NULL;
b->texture = NULL; b->texture = NULL;
b->shader = shader; b->shader = s->shader;
for (j = 0; j < MAXRLIGHTMAPS; j++) for (j = 0; j < MAXRLIGHTMAPS; j++)
b->lightmap[j] = -1; b->lightmap[j] = -1;
b->surf_first = batchid; b->surf_first = batchid;
b->flags = 0; b->flags = 0;
sort = shader->sort; sort = b->shader->sort;
//fixme: we probably need to force some blend modes based on the surface flags. //fixme: we probably need to force some blend modes based on the surface flags.
if (rent->flags & RF_FORCECOLOURMOD) if (rent->flags & RF_FORCECOLOURMOD)
b->flags |= BEF_FORCECOLOURMOD; b->flags |= BEF_FORCECOLOURMOD;
@ -755,8 +770,8 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches)
{ {
if (batchid == b->surf_first) if (batchid == b->surf_first)
{ {
tex_w = 1.0f / model.textures[modelc->skins[mesh->skinindex]].w; tex_w = 1.0f / s->w;
tex_h = 1.0f / model.textures[modelc->skins[mesh->skinindex]].h; tex_h = 1.0f / s->h;
b->mesh = &mptr; b->mesh = &mptr;
R_HL_BuildFrame(&model, amodel, b->ent, (short *) ((qbyte *) model.header + mesh->index), tex_w, tex_h, b->mesh[0]); R_HL_BuildFrame(&model, amodel, b->ent, (short *) ((qbyte *) model.header + mesh->index), tex_w, tex_h, b->mesh[0]);

View file

@ -46,8 +46,6 @@ texture_t r_notexture_mip_real;
texture_t *r_notexture_mip = &r_notexture_mip_real; texture_t *r_notexture_mip = &r_notexture_mip_real;
#endif #endif
qboolean isnotmap = true; //used to not warp ammo models.
void CM_Init(void); void CM_Init(void);
void CM_Shutdown(void); void CM_Shutdown(void);
@ -1258,7 +1256,7 @@ static const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, un
} }
void Mod_FinishTexture(texture_t *tx, const char *loadname) void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safetoloadfromwads)
{ {
#ifndef SERVERONLY #ifndef SERVERONLY
extern cvar_t gl_shadeq1_name; extern cvar_t gl_shadeq1_name;
@ -1267,6 +1265,8 @@ void Mod_FinishTexture(texture_t *tx, const char *loadname)
const char *origname = NULL; const char *origname = NULL;
const char *shadername = tx->name; const char *shadername = tx->name;
if (!safetoloadfromwads)
{
/*skies? just replace with the override sky*/ /*skies? just replace with the override sky*/
if (!strncmp(tx->name, "sky", 3) && *cl.skyname) if (!strncmp(tx->name, "sky", 3) && *cl.skyname)
@ -1297,6 +1297,15 @@ void Mod_FinishTexture(texture_t *tx, const char *loadname)
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)
return;
}
else
{ //already loaded. don't waste time / crash (this will be a dead pointer).
if (tx->mips[0])
return;
}
if (!strncmp(tx->name, "sky", 3)) if (!strncmp(tx->name, "sky", 3))
R_InitSky (tx->shader, shadername, tx->mips[0], tx->width, tx->height); R_InitSky (tx->shader, shadername, tx->mips[0], tx->width, tx->height);
else else
@ -1559,10 +1568,10 @@ void Mod_NowLoadExternal(model_t *loadmodel)
if (!tx) //e1m2, this happens if (!tx) //e1m2, this happens
continue; continue;
if (tx->shader) if (tx->mips[0])
continue; continue;
Mod_FinishTexture(tx, loadname); Mod_FinishTexture(tx, loadname, true);
} }
#endif #endif
} }
@ -1661,11 +1670,12 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
if (!litdata && r_loadlits.value) if (!litdata && r_loadlits.value)
{ {
char *litnames[] = { char *litnames[] = {
"maps/%s.lit2", "%s.lit2",
"maps/%s.lit", "%s.lit",
"lits/%s.lit2", "lits/%s.lit2",
"lits/%s.lit" "lits/%s.lit"
}; };
char litbasep[MAX_QPATH];
char litbase[MAX_QPATH]; char litbase[MAX_QPATH];
int depth; int depth;
int bestdepth = 0x7fffffff; int bestdepth = 0x7fffffff;
@ -1675,10 +1685,14 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
size_t litsize; size_t litsize;
qboolean inhibitvalidation = false; qboolean inhibitvalidation = false;
COM_StripExtension(loadmodel->name, litbase, sizeof(litbase)); COM_StripExtension(loadmodel->name, litbasep, sizeof(litbasep));
COM_FileBase(loadmodel->name, litbase, sizeof(litbase));
for (i = 0; i < sizeof(litnames)/sizeof(litnames[0]); i++) for (i = 0; i < sizeof(litnames)/sizeof(litnames[0]); i++)
{ {
if (strchr(litnames[i], '/'))
Q_snprintfz(litname, sizeof(litname), litnames[i], litbase); Q_snprintfz(litname, sizeof(litname), litnames[i], litbase);
else
Q_snprintfz(litname, sizeof(litname), litnames[i], litbasep);
depth = COM_FDepthFile(litname, false); depth = COM_FDepthFile(litname, false);
if (depth < bestdepth) if (depth < bestdepth)
{ {
@ -1688,7 +1702,10 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
} }
if (best >= 0) if (best >= 0)
{ {
if (strchr(litnames[best], '/'))
Q_snprintfz(litname, sizeof(litname), litnames[best], litbase); Q_snprintfz(litname, sizeof(litname), litnames[best], litbase);
else
Q_snprintfz(litname, sizeof(litname), litnames[best], litbasep);
litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize); litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize);
} }
else else
@ -2179,8 +2196,7 @@ qboolean Mod_LoadVertexNormals (model_t *loadmodel, qbyte *mod_base, lump_t *l)
Mod_LoadSubmodels Mod_LoadSubmodels
================= =================
*/ */
static qboolean hexen2map; qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map)
qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l)
{ {
dq1model_t *inq; dq1model_t *inq;
dh2model_t *inh; dh2model_t *inh;
@ -2193,7 +2209,7 @@ qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l)
inh = (void *)(mod_base + l->fileofs); inh = (void *)(mod_base + l->fileofs);
if (!inq->numfaces) if (!inq->numfaces)
{ {
hexen2map = true; *hexen2map = true;
if (l->filelen % sizeof(*inh)) if (l->filelen % sizeof(*inh))
{ {
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
@ -2231,7 +2247,7 @@ qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l)
} }
else else
{ {
hexen2map = false; *hexen2map = false;
if (l->filelen % sizeof(*inq)) if (l->filelen % sizeof(*inq))
{ {
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
@ -3204,7 +3220,7 @@ static void Mod_Batches_AllocLightmaps(model_t *mod)
} }
samps /= 4; samps /= 4;
samps = sqrt(samps); samps = sqrt(samps);
if (j > 128 || !r_dynamic.ival) if (j > 128 || r_dynamic.ival <= 0)
samps *= 2; samps *= 2;
mod->lightmaps.width = bound(j, samps, LMBLOCK_SIZE_MAX); mod->lightmaps.width = bound(j, samps, LMBLOCK_SIZE_MAX);
mod->lightmaps.height = bound(j, samps, LMBLOCK_SIZE_MAX); mod->lightmaps.height = bound(j, samps, LMBLOCK_SIZE_MAX);
@ -3500,7 +3516,7 @@ static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, i
Mod_LoadLeafs Mod_LoadLeafs
================= =================
*/ */
static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm, qbyte *ptr, size_t len) static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm, qboolean isnotmap, qbyte *ptr, size_t len)
{ {
mleaf_t *out; mleaf_t *out;
int i, j, count, p; int i, j, count, p;
@ -3784,7 +3800,7 @@ void Mod_LoadCrouchHull(model_t *loadmodel)
Mod_LoadClipnodes Mod_LoadClipnodes
================= =================
*/ */
qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm, qboolean hexen2map)
{ {
dsclipnode_t *ins; dsclipnode_t *ins;
dlclipnode_t *inl; dlclipnode_t *inl;
@ -4443,7 +4459,7 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b)
if (!strncmp(loadname, "b_", 2)) if (!strncmp(loadname, "b_", 2))
Q_strncpyz(loadname, "bmodels", sizeof(loadname)); Q_strncpyz(loadname, "bmodels", sizeof(loadname));
for(a = 0; a < mod->numtextures; a++) for(a = 0; a < mod->numtextures; a++)
Mod_FinishTexture(mod->textures[a], loadname); Mod_FinishTexture(mod->textures[a], loadname, false);
} }
} }
Mod_Batches_Build(mod, data); Mod_Batches_Build(mod, data);
@ -4483,7 +4499,7 @@ static void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaf
//ignore the patch file if its in a different gamedir. //ignore the patch file if its in a different gamedir.
//this file format sucks too much for other verification. //this file format sucks too much for other verification.
if (FS_FLocateFile(mod->name,FSLFRT_DEPTH_OSONLY, NULL) != FS_FLocateFile(patchname,FSLFRT_DEPTH_OSONLY, NULL)) if (FS_FLocateFile(mod->name,FSLF_DEPTH_EXPLICIT, NULL) != FS_FLocateFile(patchname,FSLF_DEPTH_EXPLICIT, NULL))
return; return;
patch->filelen = FS_LoadFile(patchname, &patch->fileptr); patch->filelen = FS_LoadFile(patchname, &patch->fileptr);
@ -4547,6 +4563,8 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize)
int longm = false; int longm = false;
char loadname[32]; char loadname[32];
qbyte *mod_base = buffer; qbyte *mod_base = buffer;
qboolean hexen2map = false;
qboolean isnotmap;
#if (defined(ODE_STATIC) || defined(ODE_DYNAMIC)) #if (defined(ODE_STATIC) || defined(ODE_DYNAMIC))
qboolean ode = true; qboolean ode = true;
#else #else
@ -4662,7 +4680,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize)
noerrors = noerrors && Mod_LoadTextures (mod, mod_base, &header->lumps[LUMP_TEXTURES]); noerrors = noerrors && Mod_LoadTextures (mod, mod_base, &header->lumps[LUMP_TEXTURES]);
} }
TRACE(("Loading Submodels\n")); TRACE(("Loading Submodels\n"));
noerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); noerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS], &hexen2map);
if (noerrors) if (noerrors)
{ {
TRACE(("Loading CH\n")); TRACE(("Loading CH\n"));
@ -4689,11 +4707,11 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize)
TRACE(("Loading Vis\n")); TRACE(("Loading Vis\n"));
Mod_LoadVisibility (mod, mod_base, &header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen); Mod_LoadVisibility (mod, mod_base, &header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen);
} }
noerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS], longm, vispatch.leafptr, vispatch.leaflen); noerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS], longm, isnotmap, vispatch.leafptr, vispatch.leaflen);
TRACE(("Loading Nodes\n")); TRACE(("Loading Nodes\n"));
noerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header->lumps[LUMP_NODES], longm); noerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header->lumps[LUMP_NODES], longm);
TRACE(("Loading Clipnodes\n")); TRACE(("Loading Clipnodes\n"));
noerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header->lumps[LUMP_CLIPNODES], longm); noerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header->lumps[LUMP_CLIPNODES], longm, hexen2map);
if (noerrors) if (noerrors)
{ {
TRACE(("Loading hull 0\n")); TRACE(("Loading hull 0\n"));

View file

@ -137,8 +137,12 @@ typedef struct batch_s
#endif #endif
/*caller-use, not interpreted by backend*/ /*caller-use, not interpreted by backend*/
union union
{
struct
{ {
unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!) unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!)
unsigned int ebobatch; //
};
struct struct
{ {
unsigned int surf_first; unsigned int surf_first;
@ -952,6 +956,7 @@ typedef struct model_s
vbo_t *vbos; vbo_t *vbos;
void *terrain; void *terrain;
batch_t *batches[SHADER_SORT_COUNT]; batch_t *batches[SHADER_SORT_COUNT];
unsigned int numbatches;
struct struct
{ {
int first; //once built... int first; //once built...

View file

@ -192,6 +192,7 @@ index_t flashblend_indexes[FLASHBLEND_VERTS*3];
index_t flashblend_fsindexes[6] = {0, 1, 2, 0, 2, 3}; index_t flashblend_fsindexes[6] = {0, 1, 2, 0, 2, 3};
mesh_t flashblend_mesh; mesh_t flashblend_mesh;
mesh_t flashblend_fsmesh; mesh_t flashblend_fsmesh;
shader_t *occluded_shader;
shader_t *flashblend_shader; shader_t *flashblend_shader;
shader_t *lpplight_shader; shader_t *lpplight_shader;
@ -264,6 +265,15 @@ void R_InitFlashblends(void)
"}\n" "}\n"
"}\n" "}\n"
); );
occluded_shader = R_RegisterShader("flashblend_occlusiontest", SUF_NONE,
"{\n"
"program defaultadditivesprite\n"
"{\n"
"maskcolor\n"
"maskalpha\n"
"}\n"
"}\n"
);
lpplight_shader = NULL; lpplight_shader = NULL;
} }
@ -401,17 +411,98 @@ void R_RenderDlights (void)
/*coronas use depth testing to compute visibility*/ /*coronas use depth testing to compute visibility*/
if (coronastyle) if (coronastyle)
{ {
extern cvar_t temp1; int method;
if (r_coronas_occlusion.ival) if (!*r_coronas_occlusion.string)
method = 4;
else
method = r_coronas_occlusion.ival;
if (method == 3 && qrenderer != QR_OPENGL)
method = 1;
if (method == 4 && (qrenderer != QR_OPENGL || !qglGenQueriesARB))
method = 1;
switch(method)
{ {
case 2:
if (TraceLineR(r_refdef.vieworg, l->origin, waste1, waste2)) if (TraceLineR(r_refdef.vieworg, l->origin, waste1, waste2))
continue; continue;
break;
default:
case 1:
if (TraceLineN(r_refdef.vieworg, l->origin, waste1, waste2))
continue;
break;
case 0:
break;
#ifdef GLQUAKE
case 3:
{
float depth;
vec3_t out;
float v[4], tempv[4];
float mvp[16];
v[0] = l->origin[0];
v[1] = l->origin[1];
v[2] = l->origin[2];
v[3] = 1;
Matrix4_Multiply(r_refdef.m_projection, r_refdef.m_view, mvp);
Matrix4x4_CM_Transform4(mvp, v, tempv);
tempv[0] /= tempv[3];
tempv[1] /= tempv[3];
tempv[2] /= tempv[3];
out[0] = (1+tempv[0])/2;
out[1] = (1+tempv[1])/2;
out[2] = (1+tempv[2])/2;
out[0] = out[0]*r_refdef.pxrect.width + r_refdef.pxrect.x;
out[1] = out[1]*r_refdef.pxrect.height + r_refdef.pxrect.y;
if (tempv[3] < 0)
out[2] *= -1;
if (out[2] < 0)
continue;
//FIXME: in terms of performance, mixing reads+draws is BAD BAD BAD. SERIOUSLY BAD
qglReadPixels(out[0], out[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
if (depth < out[2])
continue;
}
case 4:
{
GLuint res;
qboolean requery = true;
if (l->coronaocclusionquery)
{
qglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_AVAILABLE_ARB, &res);
if (res)
qglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_ARB, &l->coronaocclusionresult);
else if (!l->coronaocclusionresult)
continue; //query still running, nor currently visible.
else
requery = false;
} }
else else
{ {
if (TraceLineN(r_refdef.vieworg, l->origin, waste1, waste2)) qglGenQueriesARB(1, &l->coronaocclusionquery);
}
if (requery)
{
qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, l->coronaocclusionquery);
R_BuildDlightMesh (l, intensity*10, 0.01, coronastyle);
BE_DrawMesh_Single(occluded_shader, &flashblend_mesh, NULL, beflags);
qglEndQueryARB(GL_SAMPLES_PASSED_ARB);
}
if (!l->coronaocclusionresult)
continue; continue;
} }
#endif
}
} }
if (!R_BuildDlightMesh (l, intensity, cscale, coronastyle) && !coronastyle) if (!R_BuildDlightMesh (l, intensity, cscale, coronastyle) && !coronastyle)
@ -530,7 +621,7 @@ void R_PushDlights (void)
return; return;
#endif #endif
if (!r_dynamic.ival || !cl.worldmodel) if (r_dynamic.ival <= 0|| !cl.worldmodel)
return; return;
if (!cl.worldmodel->nodes) if (!cl.worldmodel->nodes)

View file

@ -329,6 +329,7 @@ void GLBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch
BZ_Free(vbo->vertdata); BZ_Free(vbo->vertdata);
vbo->vertdata = NULL; vbo->vertdata = NULL;
} }
vbo->vertcount = vcount;
vbo->next = *vbochain; vbo->next = *vbochain;
*vbochain = vbo; *vbochain = vbo;
@ -506,6 +507,7 @@ void GLBE_GenBrushModelVBO(model_t *mod)
BZ_Free(vbo->vertdata); BZ_Free(vbo->vertdata);
vbo->vertdata = NULL; vbo->vertdata = NULL;
} }
vbo->vertcount = vcount;
} }
#endif #endif
} }
@ -524,8 +526,8 @@ void GLBE_UploadAllLightmaps(void)
lm = lightmap[i]; lm = lightmap[i];
lm->rectchange.l = lm->width; lm->rectchange.l = lm->width;
lm->rectchange.t = lm->height; lm->rectchange.t = lm->height;
lm->rectchange.w = 0; lm->rectchange.r = 0;
lm->rectchange.h = 0; lm->rectchange.b = 0;
if (!lm->modified) if (!lm->modified)
continue; continue;
lm->modified = false; lm->modified = false;

View file

@ -212,11 +212,11 @@ void GLSCR_UpdateScreen (void)
Media_RecordFrame(); Media_RecordFrame();
#endif #endif
RSpeedShow();
if (R2D_Flush) if (R2D_Flush)
R2D_Flush(); R2D_Flush();
RSpeedEnd(RSPEED_TOTALREFRESH); RSpeedEnd(RSPEED_TOTALREFRESH);
RSpeedShow();
RSpeedRemark(); RSpeedRemark();
VID_SwapBuffers(); VID_SwapBuffers();

View file

@ -4597,6 +4597,9 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons
imageflags = (basefmt==TF_SOLID8 || basefmt == TF_MIP4_SOLID8)?IF_NOALPHA:0; imageflags = (basefmt==TF_SOLID8 || basefmt == TF_MIP4_SOLID8)?IF_NOALPHA:0;
imageflags |= IF_MIPCAP; imageflags |= IF_MIPCAP;
if (basefmt == TF_MIP4_SOLID8 && palette && palette != host_basepal)
basefmt = TF_MIP4_8PAL24;
COM_StripExtension(imagename, imagename, sizeof(imagename)); COM_StripExtension(imagename, imagename, sizeof(imagename));
aframes = max(1, shader->numdefaulttextures); aframes = max(1, shader->numdefaulttextures);
@ -4710,8 +4713,8 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons
if (!TEXVALID(tex->fullbright)) if (!TEXVALID(tex->fullbright))
{ {
int s=-1; int s=-1;
if (mipdata[0]) if (mipdata[0] && (!palette || palette == host_basepal))
for(s = width*height; s-->0; ) for(s = width*height-1; s>=0; s--)
{ {
if (mipdata[0][s] >= 256-vid.fullbright) if (mipdata[0][s] >= 256-vid.fullbright)
break; break;

View file

@ -3144,8 +3144,6 @@ static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, vec3_t axis[3],
Sh_DrawEntLighting(dl, colour); Sh_DrawEntLighting(dl, colour);
} }
void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop);
void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)
{ {
#ifdef GLQUAKE #ifdef GLQUAKE
@ -3221,7 +3219,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)
BE_SelectMode(BEM_CREPUSCULAR); BE_SelectMode(BEM_CREPUSCULAR);
BE_SelectDLight(dl, colours, dl->axis, LSHADER_STANDARD); BE_SelectDLight(dl, colours, dl->axis, LSHADER_STANDARD);
GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_BLEND);
GLBE_FBO_Pop(oldfbo); GLBE_FBO_Pop(oldfbo);

View file

@ -62,6 +62,15 @@ void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid*
BINDTEXFUNCPTR qglBindTexture; BINDTEXFUNCPTR qglBindTexture;
void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids);
void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);
//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id);
void (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id);
void (APIENTRY *qglEndQueryARB)(GLenum target);
//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params);
//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params);
void (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params);
/*glslang - arb_shader_objects /*glslang - arb_shader_objects
gl core uses different names/distinctions from the extension gl core uses different names/distinctions from the extension
*/ */
@ -1144,6 +1153,31 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
} }
#endif #endif
if (!gl_config.gles && gl_config.glversion >= 1.5)
{
qglGenQueriesARB = (void *)getglext("glGenQueries");
qglDeleteQueriesARB = (void *)getglext("glDeleteQueries");
qglBeginQueryARB = (void *)getglext("glBeginQuery");
qglEndQueryARB = (void *)getglext("glEndQuery");
qglGetQueryObjectuivARB = (void *)getglext("glGetQueryObjectuiv");
}
else if (GL_CheckExtension("GL_ARB_occlusion_query"))
{
qglGenQueriesARB = (void *)getglext("glGenQueriesARB");
qglDeleteQueriesARB = (void *)getglext("glDeleteQueriesARB");
qglBeginQueryARB = (void *)getglext("glBeginQueryARB");
qglEndQueryARB = (void *)getglext("glEndQueryARB");
qglGetQueryObjectuivARB = (void *)getglext("glGetQueryObjectuivARB");
}
else
{
qglGenQueriesARB = NULL;
qglDeleteQueriesARB = NULL;
qglBeginQueryARB = NULL;
qglEndQueryARB = NULL;
qglGetQueryObjectuivARB = NULL;
}
if (!gl_config.gles && gl_config_nofixedfunc) if (!gl_config.gles && gl_config_nofixedfunc)
qglDisableClientState(GL_VERTEX_ARRAY); qglDisableClientState(GL_VERTEX_ARRAY);
} }
@ -1822,7 +1856,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]); length[strings] = strlen(prstrings[strings]);
strings++; strings++;
} }
if (ver >= 140) if (ver >= 130) //gl3+ deprecated the varying keyword for geometry shaders to work properly
{ {
prstrings[strings] = prstrings[strings] =
"#define varying in\n" "#define varying in\n"
@ -1862,7 +1896,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]); length[strings] = strlen(prstrings[strings]);
strings++; strings++;
} }
if (ver >= 140) if (ver >= 130)
{ {
prstrings[strings] = prstrings[strings] =
"#define attribute in\n" "#define attribute in\n"
@ -1871,7 +1905,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]); length[strings] = strlen(prstrings[strings]);
strings++; strings++;
} }
if (gl_config.nofixedfunc || ver >= 140) if (gl_config.nofixedfunc || ver >= 130)
{ {
prstrings[strings] = prstrings[strings] =
"attribute vec3 v_position1;\n" "attribute vec3 v_position1;\n"

View file

@ -759,7 +759,7 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int
unsigned int stride = width; unsigned int stride = width;
width /= 2; width /= 2;
if (width < 1 || height < 1 || stride != width*2) if (width < 1 || height < 1 || stride != width*2 || !src)
return; return;
if (width*height > countof(trans)) if (width*height > countof(trans))

View file

@ -693,6 +693,15 @@ extern GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target);
extern void (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint * params); extern void (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint * params);
extern void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids);
extern void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);
//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id);
extern void (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id);
extern void (APIENTRY *qglEndQueryARB)(GLenum target);
//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params);
//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params);
extern void (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params);
//glslang - arb_shader_objects //glslang - arb_shader_objects
extern FTEPFNGLCREATEPROGRAMOBJECTARBPROC qglCreateProgramObjectARB; extern FTEPFNGLCREATEPROGRAMOBJECTARBPROC qglCreateProgramObjectARB;
extern FTEPFNGLDELETEOBJECTARBPROC qglDeleteProgramObject_; extern FTEPFNGLDELETEOBJECTARBPROC qglDeleteProgramObject_;

View file

@ -764,4 +764,14 @@ typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void);
#define GL_RGBA32F_ARB 0x8814 #define GL_RGBA32F_ARB 0x8814
#endif #endif
#ifndef GL_SAMPLES_PASSED_ARB
#define GL_SAMPLES_PASSED_ARB 0x8914
//#define GL_QUERY_COUNTER_BITS_ARB 0x8864
//#define GL_CURRENT_QUERY_ARB 0x8865
#define GL_QUERY_RESULT_ARB 0x8866
#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867
#endif
#endif #endif

View file

@ -558,8 +558,6 @@ static void LightCalcPoints (llightinfo_t *l, float lmscale)
VectorSubtract (facemid, surf, move); VectorSubtract (facemid, surf, move);
VectorNormalize (move); VectorNormalize (move);
VectorMA (surf, 8, move, surf); VectorMA (surf, 8, move, surf);
P_RunParticleEffectType(surf, NULL, 1, pt_wizspike);
} }
} }
} }

View file

@ -181,7 +181,7 @@ typedef struct
int unknown7[2]; int unknown7[2];
float unknown[4]; float unknown[4];
int unknown8; int unknown8;
int seqindex; unsigned int seqindex;
int unknown9[4]; int unknown9[4];
} hlmdl_sequencelist_t; } hlmdl_sequencelist_t;
@ -193,10 +193,17 @@ typedef struct
typedef struct typedef struct
{ {
char name[96]; /* should be split label[32] and name[64] */ char name[96]; /* should be split label[32] and name[64] */
void * cache; unsigned int cache;
int data; int data;
} hlmdl_sequencedata_t; } hlmdl_sequencedata_t;
typedef struct
{
int magic; //IDSQ
int version; //10
char name[64];
int unk1;
} hlmdl_sequencefile_t;
/* /*
----------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------
halflife model internal structure halflife model internal structure
@ -209,21 +216,27 @@ typedef struct
/* Static pointers */ /* Static pointers */
hlmdl_header_t *header; hlmdl_header_t *header;
hlmdl_header_t *texheader;
hlmdl_tex_t *textures;
hlmdl_bone_t *bones; hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls; hlmdl_bonecontroller_t *bonectls;
shader_t **shaders; struct hlmodelshaders_s *shaders;
hlmdl_sequencefile_t **animcache;
zonegroup_t *memgroup;
} hlmodel_t; } hlmodel_t;
#define MAX_ANIM_GROUPS 16 //submodel files containing anim data.
typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing
{ {
int header; hlmdl_header_t *header;
int texheader; hlmdl_bone_t *bones;
int textures; hlmdl_bonecontroller_t *bonectls;
int bones; hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS];
int bonectls; struct hlmodelshaders_s
int shaders; {
char name[MAX_QPATH];
texnums_t defaulttex;
shader_t *shader;
int w, h;
} *shaders;
short *skins; short *skins;
int numskins; int numskins;
} hlmodelcache_t; } hlmodelcache_t;

View file

@ -717,12 +717,12 @@ batch_t *GLBE_GetTempBatch(void);
void GLBE_GenBrushModelVBO(model_t *mod); void GLBE_GenBrushModelVBO(model_t *mod);
void GLBE_ClearVBO(vbo_t *vbo); void GLBE_ClearVBO(vbo_t *vbo);
void GLBE_UploadAllLightmaps(void); void GLBE_UploadAllLightmaps(void);
void GLBE_DrawWorld (qboolean drawworld, qbyte *vis); void GLBE_DrawWorld (batch_t **worldbatches, qbyte *vis);
qboolean GLBE_LightCullModel(vec3_t org, model_t *model); qboolean GLBE_LightCullModel(vec3_t org, model_t *model);
void GLBE_SelectEntity(entity_t *ent); void GLBE_SelectEntity(entity_t *ent);
qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);
void GLBE_Scissor(srect_t *rect); void GLBE_Scissor(srect_t *rect);
void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); void GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop);
//void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth); //void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth);
void GLBE_RenderToTextureUpdate2d(qboolean destchanged); void GLBE_RenderToTextureUpdate2d(qboolean destchanged);
void GLBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize); void GLBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);
@ -780,9 +780,9 @@ qboolean D3D11Shader_Init(unsigned int featurelevel);
void D3D11BE_Reset(qboolean before); void D3D11BE_Reset(qboolean before);
void D3D11BE_SetupViewCBuffer(void); void D3D11BE_SetupViewCBuffer(void);
void D3D11_UploadLightmap(lightmapinfo_t *lm); void D3D11_UploadLightmap(lightmapinfo_t *lm);
void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); void D3D11BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);
void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);
void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray);
void D3D11BE_VBO_Destroy(vboarray_t *vearray); void D3D11BE_VBO_Destroy(vboarray_t *vearray);
void D3D11BE_Scissor(srect_t *rect); void D3D11BE_Scissor(srect_t *rect);

View file

@ -307,7 +307,8 @@ void HTTP_RunExisting (void)
ammount = recv(cl->datasock, cl->inbuffer+cl->inbufferused, ammount, 0); ammount = recv(cl->datasock, cl->inbuffer+cl->inbufferused, ammount, 0);
if (ammount < 0) if (ammount < 0)
{ {
if (neterrno() != NET_EWOULDBLOCK) //they closed on us. Assume end. int e = neterrno();
if (e != NET_EWOULDBLOCK) //they closed on us. Assume end.
{ {
cl->closereason = "recv error"; cl->closereason = "recv error";
} }

View file

@ -1651,6 +1651,14 @@ char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buf, int *bufo
#undef AddS #undef AddS
} }
//just a simple helper that makes sure the s_name+s_file values are actually valid. some qccs generate really dodgy values intended to crash decompilers, but also crash debuggers too.
static char *PR_StaticString(progfuncs_t *progfuncs, string_t thestring)
{
if (thestring <= 0 || thestring >= progfuncs->funcs.stringtablesize)
return "???";
return thestring + progfuncs->funcs.stringtable;
}
char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufmax) char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufmax)
{ {
#define AddS(str) PR_Cat(buf, str, bufofs, bufmax) #define AddS(str) PR_Cat(buf, str, bufofs, bufmax)
@ -1691,9 +1699,9 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufm
AddS (buffer); AddS (buffer);
} }
if (!f->s_file) if (!f->s_file)
sprintf(buffer, "\t\"%i:%s\"\n", progs, f->s_name+progfuncs->funcs.stringtable); sprintf(buffer, "\t\"%i:%s\"\n", progs, PR_StaticString(progfuncs, f->s_name));
else else
sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, f->s_name+progfuncs->funcs.stringtable, f->s_file+progfuncs->funcs.stringtable); sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, PR_StaticString(progfuncs, f->s_name), PR_StaticString(progfuncs, f->s_file));
AddS (buffer); AddS (buffer);
AddS ("\t{\n"); AddS ("\t{\n");
@ -1707,10 +1715,10 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufm
{ {
if (local->type == ev_entity) if (local->type == ev_entity)
{ {
sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->funcs.stringtable, ((eval_t*)(globalbase - f->locals+arg))->edict); sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", PR_StaticString(progfuncs, local->s_name), ((eval_t*)(globalbase - f->locals+arg))->edict);
} }
else else
sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg), false)); sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", PR_StaticString(progfuncs, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg), false));
if (local->type == ev_vector) if (local->type == ev_vector)
arg+=2; arg+=2;

View file

@ -1072,17 +1072,17 @@ void PR_Compile_f(void)
argv[4] = Cmd_Argv(1); argv[4] = Cmd_Argv(1);
argc = 5; argc = 5;
} }
if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL))
{ {
//try the qc path //try the qc path
argv[2] = "qc"; argv[2] = "qc";
} }
if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL))
{ {
//try the progs path (yeah... gah) //try the progs path (yeah... gah)
argv[2] = "progs"; argv[2] = "progs";
} }
if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL))
{ {
//try the gamedir path //try the gamedir path
argv[1] = argv[3]; argv[1] = argv[3];
@ -3782,7 +3782,7 @@ static void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvar
G_INT(OFS_RETURN) = G_INT(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
/*touch the file, so any packs will be referenced. this is fte-specific behaviour.*/ /*touch the file, so any packs will be referenced. this is fte-specific behaviour.*/
FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); FS_FLocateFile(s, FSLF_IFFOUND, NULL);
} }
int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s)
@ -3802,7 +3802,7 @@ int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s)
Q_strncpyz(sv.strings.sound_precache[i], s, sizeof(sv.strings.sound_precache[i])); Q_strncpyz(sv.strings.sound_precache[i], s, sizeof(sv.strings.sound_precache[i]));
/*touch the file, so any packs will be referenced*/ /*touch the file, so any packs will be referenced*/
FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); FS_FLocateFile(s, FSLF_IFFOUND, NULL);
if (sv.state != ss_loading) if (sv.state != ss_loading)
{ {
@ -3868,7 +3868,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean
else else
{ {
/*touch the file, so any packs will be referenced*/ /*touch the file, so any packs will be referenced*/
FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); FS_FLocateFile(s, FSLF_IFFOUND, NULL);
} }
if (sv.state != ss_loading) if (sv.state != ss_loading)
@ -11346,6 +11346,7 @@ void PR_DumpPlatform_f(void)
VFS_PRINTF(f, "#pragma warning error Q101 /*too many parms*/\n"); VFS_PRINTF(f, "#pragma warning error Q101 /*too many parms*/\n");
VFS_PRINTF(f, "#pragma warning error Q105 /*too few parms*/\n"); VFS_PRINTF(f, "#pragma warning error Q105 /*too few parms*/\n");
VFS_PRINTF(f, "#pragma warning error Q106 /*assignment to constant/lvalue*/\n");
VFS_PRINTF(f, "#pragma warning error Q208 /*system crc unknown*/\n"); VFS_PRINTF(f, "#pragma warning error Q208 /*system crc unknown*/\n");
VFS_PRINTF(f, "#pragma warning enable F301 /*non-utf-8 strings*/\n"); VFS_PRINTF(f, "#pragma warning enable F301 /*non-utf-8 strings*/\n");
VFS_PRINTF(f, "#pragma warning enable F302 /*uninitialised locals*/\n"); VFS_PRINTF(f, "#pragma warning enable F302 /*uninitialised locals*/\n");

View file

@ -608,7 +608,7 @@ qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolea
return false; return false;
} }
if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc)) if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))
{ {
Con_Printf("Couldn't find %s.\n", name); Con_Printf("Couldn't find %s.\n", name);
return false; return false;
@ -1493,7 +1493,7 @@ void SV_Loadgame_f (void)
{ {
flocation_t loc; flocation_t loc;
char *name = va("saves/%s/game.gsv", savename); char *name = va("saves/%s/game.gsv", savename);
if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc)) if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))
Con_Printf("Couldn't find %s.\n", name); Con_Printf("Couldn't find %s.\n", name);
else if (!*loc.rawname || loc.offset) else if (!*loc.rawname || loc.offset)
Con_Printf("%s is inside a package and cannot be used by the quake2 gamecode.\n", name); Con_Printf("%s is inside a package and cannot be used by the quake2 gamecode.\n", name);

View file

@ -221,7 +221,10 @@ void MSV_MapCluster_f(void)
//child processes return 0 and fall through //child processes return 0 and fall through
memset(&sv, 0, sizeof(sv)); memset(&sv, 0, sizeof(sv));
if (atoi(Cmd_Argv(1))) Q_strncpyz(sv.modelname, Cmd_Argv(1), sizeof(sv.modelname));
if (!*sv.modelname)
Q_strncpyz(sv.modelname, "start", sizeof(sv.modelname));
if (atoi(Cmd_Argv(2)))
{ {
Con_Printf("Opening database \"%s\"\n", sqlparams[3]); Con_Printf("Opening database \"%s\"\n", sqlparams[3]);
sv.logindatabase = SQL_NewServer("sqlite", sqlparams); sv.logindatabase = SQL_NewServer("sqlite", sqlparams);
@ -626,7 +629,7 @@ void SSV_ReadFromControlServer(void)
char *addr = MSG_ReadString(); char *addr = MSG_ReadString();
int i; int i;
Con_Printf("%s: got tookplayer\n", sv.name); Con_Printf("%s: got tookplayer\n", sv.modelname);
for (i = 0; i < svs.allocated_client_slots; i++) for (i = 0; i < svs.allocated_client_slots; i++)
{ {
@ -640,19 +643,19 @@ void SSV_ReadFromControlServer(void)
{ {
if (!*addr) if (!*addr)
{ {
Con_Printf("%s: tookplayer: failed\n", sv.name); Con_Printf("%s: tookplayer: failed\n", sv.modelname);
Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo)); Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo));
} }
else else
{ {
Con_Printf("%s: tookplayer: do transfer\n", sv.name); Con_Printf("%s: tookplayer: do transfer\n", sv.modelname);
// SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr)); // SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr));
SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr)); SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr));
cl->redirect = 2; cl->redirect = 2;
} }
} }
else else
Con_Printf("%s: tookplayer: invalid player.\n", sv.name); Con_Printf("%s: tookplayer: invalid player.\n", sv.modelname);
} }
break; break;
@ -723,7 +726,7 @@ void SSV_ReadFromControlServer(void)
} }
else else
{ {
Con_Printf("%s: server full!\n", sv.name); Con_Printf("%s: server full!\n", sv.modelname);
} }
j = MSG_ReadByte(); j = MSG_ReadByte();
@ -838,7 +841,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.name); MSG_WriteString(&send, sv.modelname);
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);
@ -971,7 +974,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv
pubsubserver_t *s = NULL; pubsubserver_t *s = NULL;
if (!s) if (!s)
s = MSV_FindSubServerName(":start"); s = MSV_FindSubServerName(va(":%s", sv.modelname));
if (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s)) if (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s))
SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n");

View file

@ -739,7 +739,7 @@ void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg,
if (bits & U_COLORMAP) if (bits & U_COLORMAP)
MSG_WriteByte (msg, to->colormap); MSG_WriteByte (msg, to->colormap);
if (bits & U_SKIN) if (bits & U_SKIN)
MSG_WriteByte (msg, to->skinnum); MSG_WriteByte (msg, to->skinnum&0xff);
if (bits & U_EFFECTS) if (bits & U_EFFECTS)
MSG_WriteByte (msg, to->effects&0x00ff); MSG_WriteByte (msg, to->effects&0x00ff);
if (bits & U_ORIGIN1) if (bits & U_ORIGIN1)
@ -3647,7 +3647,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
cameras = NULL; cameras = NULL;
#ifdef HLSERVER #ifdef HLSERVER
else if (svs.gametype == GT_HALFLIFE) else if (svs.gametype == GT_HALFLIFE)
pvs = SVHL_Snapshot_SetupPVS(client, pvsbuffer, sizeof(pvsbuffer)); SVHL_Snapshot_SetupPVS(client, cameras->pvs, sizeof(cameras->pvs));
#endif #endif
else else
SV_Snapshot_SetupPVS(client, cameras); SV_Snapshot_SetupPVS(client, cameras);
@ -3682,7 +3682,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
{ {
#ifdef HLSERVER #ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE) if (svs.gametype == GT_HALFLIFE)
SVHL_Snapshot_Build(client, pack, pvs, clent, ignorepvs); SVHL_Snapshot_Build(client, pack, cameras->pvs, clent, ignorepvs);
else else
#endif #endif
SV_Snapshot_BuildQ1(client, pack, cameras, clent); SV_Snapshot_BuildQ1(client, pack, cameras, clent);

View file

@ -223,7 +223,7 @@ baseline will be transmitted
void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client); void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client);
void SVNQ_CreateBaseline (void) void SVQ1_CreateBaseline (void)
{ {
edict_t *svent; edict_t *svent;
int entnum; int entnum;
@ -1587,7 +1587,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
// create a baseline for more efficient communications // create a baseline for more efficient communications
// SV_CreateBaseline (); // SV_CreateBaseline ();
if (svprogfuncs) if (svprogfuncs)
SVNQ_CreateBaseline(); SVQ1_CreateBaseline();
#ifdef Q2SERVER #ifdef Q2SERVER
SVQ2_BuildBaselines(); SVQ2_BuildBaselines();
#endif #endif

View file

@ -1748,7 +1748,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
//some gamecode can't cope with some extensions for some reasons... and I'm too lazy to fix the code to cope. //some gamecode can't cope with some extensions for some reasons... and I'm too lazy to fix the code to cope.
if (svs.gametype == GT_HALFLIFE) if (svs.gametype == GT_HALFLIFE)
client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS; client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS; //baseline issues
// //
client->maxmodels = 256; client->maxmodels = 256;
@ -2593,6 +2593,7 @@ client_t *SVC_DirectConnect(void)
Con_TPrintf ("%s:gamecode reject\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); Con_TPrintf ("%s:gamecode reject\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
return NULL; return NULL;
} }
temp.hledict = newcl->hledict;
} }
break; break;
@ -5030,7 +5031,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
cl->rate = ISNQCLIENT(cl)?10000:2500; //an nq client cannot cope with quakeworld's default rate, and typically doesn't have rate set either. cl->rate = ISNQCLIENT(cl)?10000:2500; //an nq client cannot cope with quakeworld's default rate, and typically doesn't have rate set either.
val = Info_ValueForKey (cl->userinfo, "dupe"); val = Info_ValueForKey (cl->userinfo, "dupe");
cl->netchan.dupe = atoi(val); cl->netchan.dupe = bound(0, atoi(val), 5);
val = Info_ValueForKey (cl->userinfo, "drate"); val = Info_ValueForKey (cl->userinfo, "drate");
if (strlen(val)) if (strlen(val))

View file

@ -2509,6 +2509,7 @@ qboolean SV_Physics (void)
if (svs.gametype == GT_HALFLIFE) if (svs.gametype == GT_HALFLIFE)
{ {
SVHL_RunFrame(); SVHL_RunFrame();
sv.world.physicstime += host_frametime;
continue; continue;
} }
#endif #endif

View file

@ -1367,6 +1367,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
client->nextservertimeupdate = sv.physicstime; client->nextservertimeupdate = sv.physicstime;
*/ */
#ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE)
return;
#endif
#ifdef NQPROT #ifdef NQPROT
ent = client->edict; ent = client->edict;
if (progstype != PROG_QW) if (progstype != PROG_QW)

View file

@ -47,8 +47,8 @@ cvar_t sv_spectalk = SCVAR("sv_spectalk", "1");
cvar_t sv_mapcheck = SCVAR("sv_mapcheck", "1"); cvar_t sv_mapcheck = SCVAR("sv_mapcheck", "1");
cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full"); cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full");
cvar_t sv_antilag = CVARFD("sv_antilag", "1", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline."); cvar_t sv_antilag = CVARFD("sv_antilag", "", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline.");
cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "1", CVAR_SERVERINFO); cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "", CVAR_SERVERINFO);
#ifndef NEWSPEEDCHEATPROT #ifndef NEWSPEEDCHEATPROT
cvar_t sv_cheatpc = CVARD("sv_cheatpc", "125", "If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated."); cvar_t sv_cheatpc = CVARD("sv_cheatpc", "125", "If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated.");
cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The interval between each speed-cheat check."); cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The interval between each speed-cheat check.");
@ -57,6 +57,9 @@ 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", "0", "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", "0", "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_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_cmdlikercon = SCVAR("sv_cmdlikercon", "0"); //set to 1 to allow a password of username:password instead of the correct rcon password. cvar_t sv_cmdlikercon = SCVAR("sv_cmdlikercon", "0"); //set to 1 to allow a password of username:password instead of the correct rcon password.
cvar_t cmd_allowaccess = SCVAR("cmd_allowaccess", "0"); //set to 1 to allow cmd to execute console commands on the server. cvar_t cmd_allowaccess = SCVAR("cmd_allowaccess", "0"); //set to 1 to allow cmd to execute console commands on the server.
cvar_t cmd_gamecodelevel = SCVAR("cmd_gamecodelevel", STRINGIFY(RESTRICT_LOCAL)); //execution level which gamecode is told about (for unrecognised commands) cvar_t cmd_gamecodelevel = SCVAR("cmd_gamecodelevel", STRINGIFY(RESTRICT_LOCAL)); //execution level which gamecode is told about (for unrecognised commands)
@ -2853,7 +2856,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
} }
#endif #endif
else else
found = FS_FLocateFile(name, FSLFRT_IFFOUND, loc); found = FS_FLocateFile(name, FSLF_IFFOUND, loc);
//nexuiz names certain files as .wav but they're really .ogg on disk. //nexuiz names certain files as .wav but they're really .ogg on disk.
if (!found && replacementname) if (!found && replacementname)
@ -2865,7 +2868,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
COM_StripExtension(name, tryogg, sizeof(tryogg)); COM_StripExtension(name, tryogg, sizeof(tryogg));
COM_DefaultExtension(tryogg, ".ogg", sizeof(tryogg)); COM_DefaultExtension(tryogg, ".ogg", sizeof(tryogg));
found = FS_FLocateFile(tryogg, FSLFRT_IFFOUND, loc); found = FS_FLocateFile(tryogg, FSLF_IFFOUND, loc);
if (found) if (found)
{ {
name = *replacementname = va("%s", tryogg); name = *replacementname = va("%s", tryogg);
@ -2909,7 +2912,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
if (pakname && SV_AllowDownload(pakname)) if (pakname && SV_AllowDownload(pakname))
{ {
//return loc of the pak instead. //return loc of the pak instead.
if (FS_FLocateFile(name, FSLFRT_IFFOUND, loc)) if (FS_FLocateFile(name, FSLF_IFFOUND, loc))
{ {
//its inside a pak file, return the name of this file instead //its inside a pak file, return the name of this file instead
*replacementname = pakname; *replacementname = pakname;
@ -4367,6 +4370,14 @@ void Cmd_Spiderpig_f(void)
} }
void Cmd_Noclip_f (void) void Cmd_Noclip_f (void)
{ {
#ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE)
{
HLSV_ClientCommand(host_client);
return;
}
#endif
if (!SV_MayCheat()) if (!SV_MayCheat())
{ {
SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n"); SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n");
@ -6991,7 +7002,7 @@ void SV_ExecuteClientMessage (client_t *cl)
cl->delay = 0.2; cl->delay = 0.2;
} }
if (sv_antilag.ival) if (sv_antilag.ival || !*sv_antilag.string)
{ {
/* /*
extern cvar_t temp1; extern cvar_t temp1;
@ -7009,7 +7020,7 @@ void SV_ExecuteClientMessage (client_t *cl)
} }
cl->laggedents_count = sv.allocated_client_slots; cl->laggedents_count = sv.allocated_client_slots;
cl->laggedents_frac = sv_antilag_frac.value; cl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value;
} }
else else
cl->laggedents_count = 0; cl->laggedents_count = 0;
@ -7720,6 +7731,9 @@ void SV_UserInit (void)
Cvar_Register (&sv_spectalk, cvargroup_servercontrol); Cvar_Register (&sv_spectalk, cvargroup_servercontrol);
Cvar_Register (&sv_mapcheck, cvargroup_servercontrol); Cvar_Register (&sv_mapcheck, cvargroup_servercontrol);
Cvar_Register (&sv_minpitch, cvargroup_servercontrol);
Cvar_Register (&sv_maxpitch, cvargroup_servercontrol);
Cvar_Register (&sv_fullredirect, cvargroup_servercontrol); Cvar_Register (&sv_fullredirect, cvargroup_servercontrol);
Cvar_Register (&sv_antilag, cvargroup_servercontrol); Cvar_Register (&sv_antilag, cvargroup_servercontrol);
Cvar_Register (&sv_antilag_frac, cvargroup_servercontrol); Cvar_Register (&sv_antilag_frac, cvargroup_servercontrol);

View file

@ -47,7 +47,7 @@ int lastusermessage;
string_t QDECL GHL_AllocString(char *string) string_t QDECL GHL_AllocString(const char *string)
{ {
char *news; char *news;
bi_begin(); bi_begin();
@ -58,7 +58,7 @@ string_t QDECL GHL_AllocString(char *string)
bi_end(); bi_end();
return news - SVHL_Globals.stringbase; return news - SVHL_Globals.stringbase;
} }
int QDECL GHL_PrecacheModel(char *name) int QDECL GHL_PrecacheModel(const char *name)
{ {
int i; int i;
bi_trace(); bi_trace();
@ -69,7 +69,7 @@ int QDECL GHL_PrecacheModel(char *name)
return 0; return 0;
} }
for (i=1 ; i<MAX_MODELS ; i++) for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
{ {
if (!sv.strings.model_precache[i]) if (!sv.strings.model_precache[i])
{ {
@ -117,7 +117,7 @@ int QDECL GHL_PrecacheSound(char *name)
return 0; return 0;
} }
for (i=1 ; i<MAX_SOUNDS ; i++) for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
{ {
if (!*sv.strings.sound_precache[i]) if (!*sv.strings.sound_precache[i])
{ {
@ -204,10 +204,156 @@ void QDECL GHL_VecToAngles(float *inv, float *outa)
bi_trace(); bi_trace();
VectorAngles(inv, NULL, outa); VectorAngles(inv, NULL, outa);
} }
#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
void QDECL GHL_MoveToOrigin(hledict_t *ent, vec3_t dest, float dist, int moveflags) void QDECL GHL_MoveToOrigin(hledict_t *ent, vec3_t dest, float dist, int moveflags)
{ {
//mode 0: move_normal
//mode 1: no idea
//mode 2: test only
// float dz;
vec3_t oldorg, neworg, end;
trace_t trace;
// int i;
int eflags = ent->v.flags;
const vec3_t up = {0, 0, 1};
vec3_t move;
qboolean relink = true;
qboolean domove = true;
bi_trace(); bi_trace();
ignore("GHL_MoveToOrigin");
if (moveflags)
{ //strafe. just move directly.
VectorSubtract(dest, ent->v.origin, move);
move[2] = 0;
VectorNormalize(move);
VectorMA(ent->v.origin, dist, move, move);
}
else
{
float yaw = DEG2RAD(ent->v.angles[1]);
move[0] = cos(yaw) * dist;
move[1] = sin(yaw) * dist;
move[2] = 0;
}
// try the move
VectorCopy (ent->v.origin, oldorg);
VectorAdd (ent->v.origin, move, neworg);
#if 0
// flying monsters don't step up
if (eflags & (FL_SWIM | FL_FLY))
{
// try one move with vertical motion, then one without
for (i=0 ; i<2 ; i++)
{
VectorAdd (ent->v.origin, move, neworg);
if (!noenemy)
{
enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);
if (i == 0 && enemy->entnum)
{
VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);
dz = DotProduct(end, axis[2]);
if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/
dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];
if (dz > 40)
VectorMA(neworg, -8, up, neworg);
if (dz < 30)
VectorMA(neworg, 8, up, neworg);
}
}
trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);
if (set_move_trace)
set_move_trace(world->progs, set_trace_globs, &trace);
if (trace.fraction == 1)
{
if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))
continue; // swim monster left water
if (domove)
VectorCopy (trace.endpos, ent->v->origin);
if (relink)
World_LinkEdict (world, ent, true);
return true;
}
if (noenemy || !enemy->entnum)
break;
}
return false;
}
#endif
// push down from a step height above the wished position
VectorMA(neworg, movevars.stepheight, up, neworg);
VectorMA(neworg, movevars.stepheight*-2, up, end);
trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
if (trace.allsolid)
return false;
if (trace.startsolid)
{
//move up by an extra step, if needed
VectorMA(neworg, -movevars.stepheight, up, neworg);
trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
if (trace.allsolid || trace.startsolid)
return false;
}
if (trace.fraction == 1)
{
// if monster had the ground pulled out, go ahead and fall
if ( (int)eflags & FL_PARTIALGROUND )
{
if (domove)
{
VectorAdd (ent->v.origin, move, ent->v.origin);
if (relink)
SVHL_LinkEdict (ent, true);
ent->v.flags = (int)eflags & ~FL_ONGROUND;
}
// Con_Printf ("fall down\n");
return true;
}
return false; // walked off an edge
}
// check point traces down for dangling corners
if (domove)
VectorCopy (trace.endpos, ent->v.origin);
/* if (!World_CheckBottom (world, ent, up))
{
if ( (int)ent->v->flags & FL_PARTIALGROUND )
{ // entity had floor mostly pulled out from underneath it
// and is trying to correct
if (relink)
SVHL_LinkEdict (ent, true);
return true;
}
if (domove)
VectorCopy (oldorg, ent->v->origin);
return false;
}
*/
if ( (int)ent->v.flags & FL_PARTIALGROUND )
{
// Con_Printf ("back on ground\n");
ent->v.flags &= ~FL_PARTIALGROUND;
}
ent->v.groundentity = trace.ent;
// the move is ok
if (relink)
SVHL_LinkEdict (ent, true);
return true;
} }
unk QDECL GHL_ChangeYaw(unk){notimpf(__func__);} unk QDECL GHL_ChangeYaw(unk){notimpf(__func__);}
unk QDECL GHL_ChangePitch(unk){notimpf(__func__);} unk QDECL GHL_ChangePitch(unk){notimpf(__func__);}
@ -243,7 +389,23 @@ hledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *valu
} }
return SVHL_Edict; return SVHL_Edict;
} }
unk QDECL GHL_GetEntityIllum(unk){notimpf(__func__);} void Sh_CalcPointLight(vec3_t point, vec3_t light);
int QDECL GHL_GetEntityIllum(hledict_t *ent)
{
vec3_t diffuse, ambient, dir;
float lev = 0;
#if defined(RTLIGHTS) && !defined(SERVERONLY)
Sh_CalcPointLight(ent->v.origin, ambient);
lev += VectorLength(ambient);
if (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value)
#endif
{
sv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, ent->v.origin, ambient, diffuse, dir);
lev += (VectorLength(ambient) + VectorLength(diffuse)/2.0)/256;
}
return lev * 255; //I assume its 0-255, no idea
}
hledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius) hledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius)
{ {
int i, j; int i, j;
@ -281,7 +443,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
int best = 0, i; int best = 0, i;
float bestdist = 99999999; //HL maps are limited in size anyway float bestdist = 99999999; //HL maps are limited in size anyway
float d; float d;
int leafnum; int clusternum;
vec3_t ofs; vec3_t ofs;
hledict_t *other; hledict_t *other;
@ -290,7 +452,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
//fixme: we need to track some state //fixme: we need to track some state
//a different client should be returned each call _per ent_ (so it can be used once per frame) //a different client should be returned each call _per ent_ (so it can be used once per frame)
viewerpvs = sv.world.worldmodel->funcs.LeafPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0); viewerpvs = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0);
for (i = 0; i < svs.allocated_client_slots; i++) for (i = 0; i < svs.allocated_client_slots; i++)
{ {
@ -302,8 +464,8 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
if (svs.clients[i].spectator) if (svs.clients[i].spectator)
continue; //ignore spectators continue; //ignore spectators
leafnum = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/ clusternum = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/
if (viewerpvs[leafnum>>3] & (1<<(leafnum&7))) if (viewerpvs[clusternum>>3] & (1<<(clusternum&7)))
{ {
VectorSubtract(ed->v.origin, other->v.origin, ofs); VectorSubtract(ed->v.origin, other->v.origin, ofs);
d = DotProduct(ofs, ofs); d = DotProduct(ofs, ofs);
@ -455,11 +617,145 @@ int QDECL GHL_DropToFloor(hledict_t *ed)
VectorCopy(tr.endpos, ed->v.origin); VectorCopy(tr.endpos, ed->v.origin);
return tr.fraction != 0 && tr.fraction != 1; return tr.fraction != 0 && tr.fraction != 1;
} }
int QDECL GHL_WalkMove(hledict_t *ed, float yaw, float dist, int mode) int QDECL GHL_WalkMove(hledict_t *ent, float yaw, float dist, int mode)
{ {
//mode 0: no idea
//mode 1: no idea
//mode 2: test only
// float dz;
vec3_t oldorg, neworg, end;
trace_t trace;
// int i;
int eflags = ent->v.flags;
const vec3_t up = {0, 0, 1};
vec3_t move;
qboolean relink = mode != 2;
qboolean domove = mode != 2;
bi_trace(); bi_trace();
ignore("walkmove");
return 1; yaw = DEG2RAD(yaw);
move[0] = cos(yaw) * dist;
move[1] = sin(yaw) * dist;
move[2] = 0;
// try the move
VectorCopy (ent->v.origin, oldorg);
VectorAdd (ent->v.origin, move, neworg);
#if 0
// flying monsters don't step up
if (eflags & (FL_SWIM | FL_FLY))
{
// try one move with vertical motion, then one without
for (i=0 ; i<2 ; i++)
{
VectorAdd (ent->v.origin, move, neworg);
if (!noenemy)
{
enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);
if (i == 0 && enemy->entnum)
{
VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);
dz = DotProduct(end, axis[2]);
if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/
dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];
if (dz > 40)
VectorMA(neworg, -8, up, neworg);
if (dz < 30)
VectorMA(neworg, 8, up, neworg);
}
}
trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);
if (set_move_trace)
set_move_trace(world->progs, set_trace_globs, &trace);
if (trace.fraction == 1)
{
if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))
continue; // swim monster left water
if (domove)
VectorCopy (trace.endpos, ent->v->origin);
if (relink)
World_LinkEdict (world, ent, true);
return true;
}
if (noenemy || !enemy->entnum)
break;
}
return false;
}
#endif
// push down from a step height above the wished position
VectorMA(neworg, movevars.stepheight, up, neworg);
VectorMA(neworg, movevars.stepheight*-2, up, end);
trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
if (trace.allsolid)
return false;
if (trace.startsolid)
{
//move up by an extra step, if needed
VectorMA(neworg, -movevars.stepheight, up, neworg);
trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
if (trace.allsolid || trace.startsolid)
return false;
}
if (trace.fraction == 1)
{
// if monster had the ground pulled out, go ahead and fall
if ( (int)eflags & FL_PARTIALGROUND )
{
if (domove)
{
VectorAdd (ent->v.origin, move, ent->v.origin);
if (relink)
SVHL_LinkEdict (ent, true);
ent->v.flags = (int)eflags & ~FL_ONGROUND;
}
// Con_Printf ("fall down\n");
return true;
}
return false; // walked off an edge
}
// check point traces down for dangling corners
if (domove)
VectorCopy (trace.endpos, ent->v.origin);
/* if (!World_CheckBottom (world, ent, up))
{
if ( (int)ent->v->flags & FL_PARTIALGROUND )
{ // entity had floor mostly pulled out from underneath it
// and is trying to correct
if (relink)
SVHL_LinkEdict (ent, true);
return true;
}
if (domove)
VectorCopy (oldorg, ent->v->origin);
return false;
}
*/
if ( (int)ent->v.flags & FL_PARTIALGROUND )
{
// Con_Printf ("back on ground\n");
ent->v.flags &= ~FL_PARTIALGROUND;
}
ent->v.groundentity = trace.ent;
// the move is ok
if (relink)
SVHL_LinkEdict (ent, true);
return true;
} }
void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg) void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg)
{ {
@ -470,12 +766,14 @@ void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg)
void QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch) void QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch)
{ {
bi_trace(); bi_trace();
SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch); if (*soundname == '!')
return; //would need us to parse sound/sentances.txt I guess
SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch, 0, 0);
} }
void QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch) void QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch)
{ {
bi_trace(); bi_trace();
SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, 0); SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, pitch, 0, 0);
} }
void QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result) void QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result)
{ {
@ -522,7 +820,7 @@ char *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end)
{ {
trace_t tr; trace_t tr;
bi_trace(); bi_trace();
sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, 0, NULL, start, end, vec3_origin, vec3_origin, MASK_WORLDSOLID, &tr); sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, 0, NULL, start, end, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr);
return tr.surface->name; return tr.surface->name;
} }
unk QDECL GHL_TraceSphere(unk){notimpf(__func__);} unk QDECL GHL_TraceSphere(unk){notimpf(__func__);}
@ -530,6 +828,8 @@ unk QDECL GHL_GetAimVector(unk){notimpf(__func__);}
void QDECL GHL_ServerCommand(char *cmd) void QDECL GHL_ServerCommand(char *cmd)
{ {
bi_trace(); bi_trace();
if (!strcmp(cmd, "reload\n"))
cmd = "restart\n";
Cbuf_AddText(cmd, RESTRICT_PROGS); Cbuf_AddText(cmd, RESTRICT_PROGS);
} }
void QDECL GHL_ServerExecute(void) void QDECL GHL_ServerExecute(void)
@ -541,8 +841,9 @@ unk QDECL GHL_ClientCommand(unk){notimpf(__func__);}
unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);} unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);}
void QDECL GHL_LightStyle(int stylenum, char *stylestr) void QDECL GHL_LightStyle(int stylenum, char *stylestr)
{ {
vec3_t rgb = {1,1,1};
bi_trace(); bi_trace();
PF_applylightstyle(stylenum, stylestr, 7); PF_applylightstyle(stylenum, stylestr, rgb);
} }
int QDECL GHL_DecalIndex(char *decalname) int QDECL GHL_DecalIndex(char *decalname)
{ {
@ -619,6 +920,9 @@ void QDECL GHL_MessageEnd(unk)
case MSG_MULTICAST+1: case MSG_MULTICAST+1:
SV_Multicast(svhl_messageorigin, MULTICAST_PHS); SV_Multicast(svhl_messageorigin, MULTICAST_PHS);
break; break;
case 9:
//spectators only
break;
default: default:
Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest); Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest);
break; break;
@ -629,7 +933,7 @@ void QDECL GHL_MessageEnd(unk)
void QDECL GHL_WriteByte(int value) void QDECL GHL_WriteByte(int value)
{ {
bi_trace(); bi_trace();
MSG_WriteByte(&sv.multicast, value); MSG_WriteByte(&sv.multicast, value & 0xff);
} }
void QDECL GHL_WriteChar(int value) void QDECL GHL_WriteChar(int value)
{ {
@ -730,7 +1034,102 @@ int QDECL GHL_RegUserMsg(char *msgname, int msgsize)
return lastusermessage--; return lastusermessage--;
} }
unk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);} unk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);}
unk QDECL GHL_GetBonePosition(unk){notimpf(__func__);}
static void GHL_GetFrameState(hledict_t *ent, framestate_t *fstate)
{
memset(fstate, 0, sizeof(*fstate));
fstate->g[FS_REG].frametime[0] = (SVHL_Globals.time - ent->v.framestarttime) * ent->v.framerate;
fstate->g[FS_REG].frame[0] = ent->v.frame;
fstate->g[FS_REG].lerpweight[0] = 1;
fstate->g[FS_REG].subblendfrac = ent->v.blending[0]; //fixme: which is upper?
//FIXME: no lower parts.
fstate->bonecontrols[0] = ent->v.controller[0] / 255.0;
fstate->bonecontrols[1] = ent->v.controller[1] / 255.0;
fstate->bonecontrols[2] = ent->v.controller[2] / 255.0;
fstate->bonecontrols[3] = ent->v.controller[3] / 255.0;
}
static void bonemat_fromqcvectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])
{
out[0] = vx[0];
out[1] = -vy[0];
out[2] = vz[0];
out[3] = t[0];
out[4] = vx[1];
out[5] = -vy[1];
out[6] = vz[1];
out[7] = t[1];
out[8] = vx[2];
out[9] = -vy[2];
out[10] = vz[2];
out[11] = t[2];
}
static void bonemat_fromidentity(float *out)
{
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = 1;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 1;
out[11] = 0;
}
static void bonemat_toqcvectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3])
{
vx[0] = in[0];
vx[1] = in[4];
vx[2] = in[8];
vy[0] = -in[1];
vy[1] = -in[5];
vy[2] = -in[9];
vz[0] = in[2];
vz[1] = in[6];
vz[2] = in[10];
t [0] = in[3];
t [1] = in[7];
t [2] = in[11];
}
static void bonemat_fromhlentity(hledict_t *ed, float *trans)
{
vec3_t d[3], a;
a[0] = -ed->v.angles[0];
a[1] = ed->v.angles[1];
a[2] = ed->v.angles[2];
AngleVectors(a, d[0], d[1], d[2]);
bonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v.origin);
}
void QDECL GHL_GetBonePosition(hledict_t *ed, int bone, vec3_t org, vec3_t ang)
{
float transent[12];
float transforms[12];
float result[12];
model_t *mod = sv.models[ed->v.modelindex];
vec3_t axis[3];
framestate_t fstate;
GHL_GetFrameState(ed, &fstate);
bone += 1; //I *think* the bones are 0-based unlike our tag-based bone numbers
if (!Mod_GetTag(mod, bone, &fstate, transforms))
{
bonemat_fromidentity(transforms);
}
bonemat_fromhlentity(ed, transent);
R_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);
bonemat_toqcvectors(result, axis[0], axis[1], axis[2], org);
VectorAngles(axis[0], axis[2], ang);
}
hlintptr_t QDECL GHL_FunctionFromName(char *name) hlintptr_t QDECL GHL_FunctionFromName(char *name)
{ {
@ -772,7 +1171,7 @@ int QDECL GHL_Cmd_Argc(unk)
unk QDECL GHL_GetAttachment(unk){notimpf(__func__);} unk QDECL GHL_GetAttachment(unk){notimpf(__func__);}
void QDECL GHL_CRC32_Init(hlcrc_t *crc) void QDECL GHL_CRC32_Init(hlcrc_t *crc)
{ {
unsigned short crc16 = *crc; unsigned short crc16;
bi_trace(); bi_trace();
QCRC_Init(&crc16); QCRC_Init(&crc16);
*crc = crc16; *crc = crc16;
@ -1324,11 +1723,12 @@ void SV_ReadLibListDotGam(void)
char value[1024]; char value[1024];
char *file; char *file;
char *s; char *s;
size_t fsize;
Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info)); Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info));
Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info)); Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info));
file = COM_LoadTempFile("liblist.gam"); file = COM_LoadTempFile("liblist.gam", &fsize);
if (!file) if (!file)
return; return;
@ -1368,6 +1768,8 @@ int SVHL_InitGame(void)
{NULL, NULL} {NULL, NULL}
}; };
memset(&SVHL_Globals, 0, sizeof(SVHL_Globals));
if (sizeof(long) != sizeof(void*)) if (sizeof(long) != sizeof(void*))
{ {
Con_Printf("sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\n"); Con_Printf("sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\n");
@ -1387,7 +1789,8 @@ int SVHL_InitGame(void)
gamedll = Info_ValueForKey(svs.info, "*gamedll"); gamedll = Info_ValueForKey(svs.info, "*gamedll");
iterator = NULL; iterator = NULL;
while(COM_IteratePaths(&iterator, path, sizeof(path))) //FIXME: game dlls from game paths are evil/exploitable.
while(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0))
{ {
snprintf (fullname, sizeof(fullname), "%s%s", path, gamedll); snprintf (fullname, sizeof(fullname), "%s%s", path, gamedll);
hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs); hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs);
@ -1404,7 +1807,7 @@ int SVHL_InitGame(void)
if (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION)) if (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION))
{ {
Con_Printf(CON_ERROR "Error: %s is incompatible (FTE is compiled for %i)\n", fullname, HALFLIFE_API_VERSION); Con_Printf(CON_ERROR "Error: %s is incompatible (Engine is compiled for %i)\n", fullname, HALFLIFE_API_VERSION);
if (GetEntityAPI(&SVHL_GameFuncs, 138)) if (GetEntityAPI(&SVHL_GameFuncs, 138))
Con_Printf(CON_ERROR "mod is 138\n"); Con_Printf(CON_ERROR "mod is 138\n");
Sys_CloseLibrary(hlgamecode); Sys_CloseLibrary(hlgamecode);
@ -1446,7 +1849,12 @@ void SVHL_SpawnEntities(char *entstring)
SVHL_Globals.deathmatch = deathmatch.value; SVHL_Globals.deathmatch = deathmatch.value;
SVHL_Globals.coop = coop.value; SVHL_Globals.coop = coop.value;
SVHL_Globals.serverflags = 0; SVHL_Globals.serverflags = 0;
SVHL_Globals.mapname = GHL_AllocString(sv.name); if (!strncmp(sv.modelname, "maps/", 5))
COM_StripExtension(sv.modelname+5, value, sizeof(value));
else
COM_StripExtension(sv.modelname, value, sizeof(value));
SVHL_Globals.mapname = GHL_AllocString(value);
SVHL_Globals.time = 0;
SVHL_NumActiveEnts = 0; SVHL_NumActiveEnts = 0;
@ -1474,8 +1882,12 @@ void SVHL_SpawnEntities(char *entstring)
sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array
for (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++) for (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++)
{ {
sv.strings.model_precache[1+i] = localmodels[i]; const char *s = va("*%i", i);
sv.models[i+1] = Mod_ForName (Mod_FixName(localmodels[i], sv.modelname), false); char *n;
n = ZG_Malloc(&hlmapmemgroup, strlen(s)+1);
strcpy(n, s);
sv.strings.model_precache[1+i] = n;
sv.models[i+1] = Mod_ForName (Mod_FixName(n, sv.modelname), false);
} }
while (entstring) while (entstring)
@ -1574,6 +1986,19 @@ qboolean HLSV_ClientCommand(client_t *client)
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1]; hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
if (!hlgamecode) if (!hlgamecode)
return false; return false;
if (!strcmp("noclip", Cmd_Argv(0)))
{
if (ed->v.movetype != MOVETYPE_NOCLIP)
ed->v.movetype = MOVETYPE_NOCLIP;
else
ed->v.movetype = MOVETYPE_WALK;
return true;
}
if (!strcmp("kill", Cmd_Argv(0)))
{
SVHL_GameFuncs.ClientKill(ed);
return true;
}
bi_begin(); bi_begin();
SVHL_GameFuncs.ClientCommand(ed); SVHL_GameFuncs.ClientCommand(ed);
bi_end(); bi_end();
@ -1589,7 +2014,8 @@ qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[1
sv.skipbprintclient = client; sv.skipbprintclient = client;
bi_begin(); bi_begin();
result = SVHL_GameFuncs.ClientConnect(&SVHL_Edict[client-svs.clients+1], client->name, ipadr, rejectmessage); client->hledict = &SVHL_Edict[client-svs.clients+1];
result = SVHL_GameFuncs.ClientConnect(client->hledict, client->name, ipadr, rejectmessage);
bi_end(); bi_end();
sv.skipbprintclient = NULL; sv.skipbprintclient = NULL;
@ -1602,7 +2028,7 @@ void SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss)
si[STAT_HEALTH] = ed->v.health; si[STAT_HEALTH] = ed->v.health;
si[STAT_VIEWHEIGHT] = ed->v.view_ofs[2]; si[STAT_VIEWHEIGHT] = ed->v.view_ofs[2];
si[STAT_WEAPON] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex); si[STAT_WEAPONMODELI] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex);
si[STAT_ITEMS] = ed->v.weapons; si[STAT_ITEMS] = ed->v.weapons;
} }
@ -1621,6 +2047,7 @@ void SVHL_DropClient(client_t *drop)
bi_begin(); bi_begin();
SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]); SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]);
bi_end(); bi_end();
drop->hledict = NULL;
ed->isfree = true; ed->isfree = true;
} }
@ -1636,8 +2063,9 @@ extern cvar_t temp1;
usercmd_t cmd = *ucmd; usercmd_t cmd = *ucmd;
cmd.msec = ucmd->msec/2; cmd.msec = ucmd->msec/2;
cmd.msec_compat = floor(cmd.msec);
SVHL_RunCmdR (ed, &cmd); SVHL_RunCmdR (ed, &cmd);
cmd.msec = ucmd->msec/2 + (ucmd->msec&1); //give them back their msec. cmd.msec_compat = ucmd->msec - cmd.msec_compat;
cmd.impulse = 0; cmd.impulse = 0;
SVHL_RunCmdR (ed, &cmd); SVHL_RunCmdR (ed, &cmd);
return; return;
@ -1649,7 +2077,22 @@ extern cvar_t temp1;
host_frametime = 0.1; host_frametime = 0.1;
pmove.cmd = *ucmd; pmove.cmd = *ucmd;
pmove.pm_type = temp1.value;//PM_NORMAL;//FLY; switch(ed->v.movetype)
{
default:
case MOVETYPE_WALK:
pmove.pm_type = PM_NORMAL;
break;
case MOVETYPE_FLY:
pmove.pm_type = PM_FLY;
break;
case MOVETYPE_NOCLIP:
pmove.pm_type = PM_SPECTATOR;
break;
case MOVETYPE_NONE:
pmove.pm_type = PM_NONE;
break;
}
pmove.numphysent = 1; pmove.numphysent = 1;
pmove.physents[0].model = sv.world.worldmodel; pmove.physents[0].model = sv.world.worldmodel;
pmove.physents[0].info = 0; pmove.physents[0].info = 0;
@ -1742,8 +2185,8 @@ extern cvar_t temp1;
} }
} }
VectorCopy(ed->v.mins, player_mins); VectorCopy(ed->v.mins, pmove.player_mins);
VectorCopy(ed->v.maxs, player_maxs); VectorCopy(ed->v.maxs, pmove.player_maxs);
VectorCopy(ed->v.origin, pmove.origin); VectorCopy(ed->v.origin, pmove.origin);
VectorCopy(ed->v.velocity, pmove.velocity); VectorCopy(ed->v.velocity, pmove.velocity);
@ -1823,6 +2266,8 @@ void SVHL_RunCmd(client_t *cl, usercmd_t *ucmd)
ed->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]); ed->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]);
ed->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]); ed->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]);
if (IS_NAN(ed->v.velocity[0]) || IS_NAN(ed->v.velocity[1]) || IS_NAN(ed->v.velocity[2]))
VectorClear(ed->v.velocity);
bi_begin(); bi_begin();
SVHL_GameFuncs.PlayerPreThink(ed); SVHL_GameFuncs.PlayerPreThink(ed);
@ -1890,12 +2335,43 @@ void SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs,
s->number = i; s->number = i;
s->modelindex = e->v.modelindex; s->modelindex = e->v.modelindex;
s->frame = e->v.sequence1; s->frame = e->v.sequence1;
s->effects = e->v.effects; s->effects = e->v.effects & 0x0f;
s->dpflags = 0;
s->skinnum = e->v.skin; s->skinnum = e->v.skin;
s->scale = 16; s->scale = 16;
s->trans = 255; s->trans = 255;
s->colormod[0] = nullentitystate.colormod[0];
s->colormod[1] = nullentitystate.colormod[1];
s->colormod[2] = nullentitystate.colormod[2];
VectorCopy(e->v.angles, s->angles); VectorCopy(e->v.angles, s->angles);
VectorCopy(e->v.origin, s->origin); VectorCopy(e->v.origin, s->origin);
if (!e->v.velocity[0] && !e->v.velocity[1] && !e->v.velocity[2])
s->dpflags |= RENDER_STEP;
s->trans = e->v.renderamt*255;
switch (e->v.rendermode)
{
case 0:
s->trans = 255;
break;
case 1: //used on laser beams, apparently
break;
case 2: //transparent windows.
break;
case 3: //used on coronarey sprites.
s->effects |= NQEF_ADDITIVE;
break;
case 4: //used on fence textures, apparently. we already deal with these clientside.
s->trans = 255;
break;
case 5: //used on the torch at the start.
s->effects |= NQEF_ADDITIVE;
break;
default:
Con_Printf("Rendermode %s %i\n", SVHL_Globals.stringbase+e->v.model, e->v.rendermode);
break;
}
} }
} }

View file

@ -417,7 +417,7 @@ typedef struct
unk (QDECL *ChangeYaw)(unk); unk (QDECL *ChangeYaw)(unk);
unk (QDECL *ChangePitch)(unk); unk (QDECL *ChangePitch)(unk);
hledict_t *(QDECL *FindEntityByString)(hledict_t *last, char *field, char *value); hledict_t *(QDECL *FindEntityByString)(hledict_t *last, char *field, char *value);
unk (QDECL *GetEntityIllum)(unk); int (QDECL *GetEntityIllum)(hledict_t *ent);
hledict_t *(QDECL *FindEntityInSphere)(hledict_t *last, float *org, float radius); hledict_t *(QDECL *FindEntityInSphere)(hledict_t *last, float *org, float radius);
hledict_t *(QDECL *FindClientInPVS)(hledict_t *ed); hledict_t *(QDECL *FindClientInPVS)(hledict_t *ed);
unk (QDECL *EntitiesInPVS)(unk); unk (QDECL *EntitiesInPVS)(unk);
@ -479,7 +479,7 @@ typedef struct
void *(QDECL *GetModelPtr)(hledict_t *ed); void *(QDECL *GetModelPtr)(hledict_t *ed);
int (QDECL *RegUserMsg)(char *msgname, int msgsize); int (QDECL *RegUserMsg)(char *msgname, int msgsize);
unk (QDECL *AnimationAutomove)(unk); unk (QDECL *AnimationAutomove)(unk);
unk (QDECL *GetBonePosition)(unk); void (QDECL *GetBonePosition)(hledict_t *ed, int bone, vec3_t org, vec3_t ang);
hlintptr_t (QDECL *FunctionFromName)(char *name); hlintptr_t (QDECL *FunctionFromName)(char *name);
char *(QDECL *NameForFunction)(hlintptr_t); char *(QDECL *NameForFunction)(hlintptr_t);
unk (QDECL *ClientPrintf)(unk); unk (QDECL *ClientPrintf)(unk);

View file

@ -552,8 +552,8 @@ qboolean SVHL_PushAngles (hledict_t *pusher, vec3_t move, vec3_t amove)
// see if the ent's bbox is inside the pusher's final position // see if the ent's bbox is inside the pusher's final position
// if (!SVHL_TestEntityPosition (check)) if (!SVHL_TestEntityPosition (check))
// continue; continue;
} }
if ((pusher->v.movetype == MOVETYPE_PUSH) || (check->v.groundentity == pusher)) if ((pusher->v.movetype == MOVETYPE_PUSH) || (check->v.groundentity == pusher))
@ -886,7 +886,7 @@ Entities that are "stuck" to another entity
*/ */
void SVHL_Physics_Follow (hledict_t *ent) void SVHL_Physics_Follow (hledict_t *ent)
{ {
vec3_t vf, vr, vu, angles, v; // vec3_t vf, vr, vu, angles, v;
hledict_t *e; hledict_t *e;
// regular thinking // regular thinking
@ -1817,6 +1817,8 @@ void SVHL_RunFrame (void)
//only run physics tics if there's a client on the server. //only run physics tics if there's a client on the server.
//this fixes the bug where the train moves out before the player spawns, so the player doesn't fall to his death //this fixes the bug where the train moves out before the player spawns, so the player doesn't fall to his death
if (sv.state == ss_active)
{
for (i = 0; i < sv.allocated_client_slots; i++) for (i = 0; i < sv.allocated_client_slots; i++)
{ {
if (svs.clients[i].state == cs_spawned) if (svs.clients[i].state == cs_spawned)
@ -1824,6 +1826,7 @@ void SVHL_RunFrame (void)
} }
if (i == sv.allocated_client_slots) if (i == sv.allocated_client_slots)
return; return;
}
SVHL_Globals.frametime = host_frametime; SVHL_Globals.frametime = host_frametime;

View file

@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "svhl_gcapi.h" #include "svhl_gcapi.h"
hull_t *World_HullForBox (vec3_t mins, vec3_t maxs); hull_t *World_HullForBox (vec3_t mins, vec3_t maxs);
qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask); //qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask);
/* /*
entities never clip against themselves, or their owner entities never clip against themselves, or their owner
@ -380,12 +380,12 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t
if (ent->v.solid != SOLID_BSP) if (ent->v.solid != SOLID_BSP)
{ {
ent->v.angles[0]*=-1; //carmack made bsp models rotate wrongly. ent->v.angles[0]*=-1; //carmack made bsp models rotate wrongly.
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask); World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask);
ent->v.angles[0]*=-1; ent->v.angles[0]*=-1;
} }
else else
{ {
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask); World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask);
} }
// fix trace up by the offset // fix trace up by the offset
@ -396,7 +396,7 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t
//okay, we hit the bbox //okay, we hit the bbox
model_t *model; model_t *model;
if (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_MODELS) if (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_PRECACHE_MODELS)
SV_Error("SV_ClipMoveToEntity: modelindex out of range\n"); SV_Error("SV_ClipMoveToEntity: modelindex out of range\n");
model = sv.models[ (int)ent->v.modelindex ]; model = sv.models[ (int)ent->v.modelindex ];
if (!model) if (!model)
@ -408,7 +408,7 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t
if (model && model->funcs.NativeTrace) if (model && model->funcs.NativeTrace)
{ {
//do the second trace //do the second trace
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID); World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID);
} }
} }

View file

@ -2097,7 +2097,7 @@ qboolean QDECL World_RegisterPhysicsEngine(const char *enginename, void(QDECL*st
world_current_physics_engine = startupfunc; world_current_physics_engine = startupfunc;
return true; return true;
} }
static void World_ShutdownPhysics(world_t *world) void World_RBE_Shutdown(world_t *world)
{ {
unsigned int u; unsigned int u;
wedict_t *ed; wedict_t *ed;
@ -2124,11 +2124,11 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename)
#if defined(CSQC_DAT) && !defined(SERVERONLY) #if defined(CSQC_DAT) && !defined(SERVERONLY)
{ {
extern world_t csqc_world; extern world_t csqc_world;
World_ShutdownPhysics(&csqc_world); World_RBE_Shutdown(&csqc_world);
} }
#endif #endif
#if !defined(CLIENTONLY) #if !defined(CLIENTONLY)
World_ShutdownPhysics(&sv.world); World_RBE_Shutdown(&sv.world);
#endif #endif
world_current_physics_engine = NULL; world_current_physics_engine = NULL;
@ -2136,12 +2136,15 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename)
void World_RBE_Start(world_t *world) void World_RBE_Start(world_t *world)
{ {
if (world_current_physics_engine) if (world_current_physics_engine)
{
if (world->worldmodel)
world_current_physics_engine(world); world_current_physics_engine(world);
} }
}
void World_Destroy(world_t *world) void World_Destroy(world_t *world)
{ {
World_ShutdownPhysics(world); World_RBE_Shutdown(world);
Z_Free(world->areanodes); Z_Free(world->areanodes);
world->areanodes = NULL; world->areanodes = NULL;

View file

@ -375,7 +375,6 @@ void SWBE_SelectMode(backendmode_t mode)
void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh) void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh)
{ {
extern cvar_t temp1;
int i; int i;
vecV_t *xyz; vecV_t *xyz;

View file

@ -110,6 +110,7 @@ int(int mod, brushface_t *faces, int numfaces, int contents) brush_history_creat
h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents);
if (h->id)
history++; history++;
history_max = history; //always break any pending redos history_max = history; //always break any pending redos
if (history_min < history_max - historyring.length) if (history_min < history_max - historyring.length)
@ -941,6 +942,75 @@ void(brushface_t *faces, int numfaces) DebrushifyLite =
} }
}; };
void(vector org, vector ang) editor_brushes_simpleclone =
{
vector midpoint;
if (!selectedbrush)
return;
tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents);
if (ang != '0 0 0')
{
brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1);
brushface_translate(tmp_faces, tmp_numfaces, -midpoint);
makevectors(ang);
brushface_rotate(tmp_faces, tmp_numfaces);
brushface_translate(tmp_faces, tmp_numfaces, midpoint + org);
}
else
brushface_translate(tmp_faces, tmp_numfaces, org);
selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents);
};
void() brushedit_subtract =
{
int discard;
vector selnormals[tmp_faces.length];
float seldists[tmp_faces.length];
int planecount = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents);
for (int i = 0; i < planecount; i++)
{
selnormals[i] = tmp_faces[i].planenormal;
seldists[i] = tmp_faces[i].planedist;
}
int numbrushes = brush_findinvolume(selectedbrushmodel, selnormals, seldists, planecount, brushlist, __NULL__, brushlist.length);
while (numbrushes --> 0)
{
int br = brushlist[numbrushes];
if (br == selectedbrush)
continue;
int counto = brush_get(selectedbrushmodel, br, tmp_faces, tmp_faces.length, &tmp_contents);
int counts = counto + brush_get(selectedbrushmodel, selectedbrush, tmp_faces+counto, tmp_faces.length-counto, &discard);
brush_history_delete(selectedbrushmodel, br);
while(counts --> counto)
{
//only consider the resulting brush if the new face actually contributed anything.
//this reduces dupes.
if (brush_calcfacepoints(1+counts, tmp_faces, counts+1, facepoints, facepoints.length))
{
//determine the brush defined by this plane
tmp_faces[counts].planenormal *= -1;
tmp_faces[counts].planedist *= -1;
brush_history_create(selectedbrushmodel, tmp_faces, counts+1, tmp_contents);
}
}
}
};
void() brushedit_resettextures =
{
int planecount = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents);
for (int i = 0; i < planecount; i++)
reset_texturecoords(&tmp_faces[i]);
brush_history_delete(selectedbrushmodel, selectedbrush);
selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, planecount, tmp_contents);
};
void(vector mousepos) editor_brushes_add = void(vector mousepos) editor_brushes_add =
{ {
vector col = '0 0 0'; vector col = '0 0 0';
@ -1482,29 +1552,12 @@ brusheditormodes
registercommand("brushedit_matchface"); registercommand("brushedit_matchface");
registercommand("brushedit_resettexcoords"); registercommand("brushedit_resettexcoords");
registercommand("brushedit_settexture"); registercommand("brushedit_settexture");
registercommand("brushedit_subtract");
registercommand("brushedit_binds"); registercommand("brushedit_binds");
registercommand("+brushedit_nogrid"); registercommand("+brushedit_nogrid");
registercommand("-brushedit_nogrid"); registercommand("-brushedit_nogrid");
}; };
void(vector org, vector ang) editor_brushes_simpleclone =
{
vector midpoint;
if (!selectedbrush)
return;
tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents);
if (ang != '0 0 0')
{
brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1);
brushface_translate(tmp_faces, tmp_numfaces, -midpoint);
makevectors(ang);
brushface_rotate(tmp_faces, tmp_numfaces);
brushface_translate(tmp_faces, tmp_numfaces, midpoint + org);
}
else
brushface_translate(tmp_faces, tmp_numfaces, org);
selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents);
};
float() editor_brushes_command = float() editor_brushes_command =
{ {
switch(argv(0)) switch(argv(0))
@ -1537,15 +1590,16 @@ brusheditormodes
bt_points = 0; bt_points = 0;
break; break;
case "brushedit_resettexcoords": case "brushedit_resettexcoords":
//IMPLEMENTME brushedit_resettextures();
brushtool = BT_NONE;
bt_points = 0;
break; break;
case "brushedit_settexture": case "brushedit_settexture":
//IMPLEMENTME //IMPLEMENTME
brushtool = BT_NONE; brushtool = BT_NONE;
bt_points = 0; bt_points = 0;
break; break;
case "brushedit_subtract":
brushedit_subtract();
break;
case "brushedit_delete": case "brushedit_delete":
if (selectedbrushmodel && selectedbrush) if (selectedbrushmodel && selectedbrush)
brush_history_delete(selectedbrushmodel, selectedbrush); brush_history_delete(selectedbrushmodel, selectedbrush);
@ -1553,15 +1607,16 @@ brusheditormodes
break; break;
case "brushedit_binds": case "brushedit_binds":
localcmd("conmenu \"\"\n"); localcmd("conmenu \"\"\n");
localcmd("menubind 0 8 \"brushedit_create\" \"Creation Tool\"\n"); float foo = 0;
localcmd("menubind 0 16 \"brushedit_slice\" \"Slice Tool\"\n"); localcmd(sprintf("menubind 0 %g \"brushedit_create\" \"Creation Tool\"\n", foo+=8));
localcmd("menubind 0 24 \"brushedit_delete\" \"Delete\"\n"); localcmd(sprintf("menubind 0 %g \"brushedit_slice\" \"Slice Tool\"\n", foo+=8));
localcmd("menubind 0 32 \"brushedit_undo\" \"Undo\"\n"); localcmd(sprintf("menubind 0 %g \"brushedit_delete\" \"Delete\"\n", foo+=8));
localcmd("menubind 0 40 \"brushedit_redo\" \"Redo\"\n"); localcmd(sprintf("menubind 0 %g \"brushedit_subtract\" \"Subtract\"\n", foo+=8));
localcmd("menubind 0 48 \"brushedit_nogrid\" \"Disable Grid\"\n"); localcmd(sprintf("menubind 0 %g \"brushedit_undo\" \"Undo\"\n", foo+=8));
localcmd(sprintf("menubind 0 %g \"brushedit_redo\" \"Redo\"\n", foo+=8));
localcmd(sprintf("menubind 0 %g \"brushedit_nogrid\" \"Disable Grid\"\n", foo+=8));
float foo = 56; #define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo+=8));
#define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo)); foo+=8;
brusheditormodes brusheditormodes
#undef brusheditormode #undef brusheditormode
break; break;
@ -1804,11 +1859,5 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
return TRUE; return TRUE;
} }
/* if (key == 's') return FALSE;
{
//CSG subtraction is actually quite easy...
//for each brush that intersects us, split it by every single one of our planes that intesect
//drop the resulting brushes if they contain contain points only within the subtraction region
}
*/ return FALSE;
}; };

View file

@ -133,6 +133,10 @@ inwater <effectname>
Specifies a replacement effect to use when this one is spawned underwater. Specifies a replacement effect to use when this one is spawned underwater.
assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains). assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains).
overwater
specifies that this
underwater
colorindex <index> [rand] colorindex <index> [rand]
Specifies a palette index to spawn the particle with. Specifies a palette index to spawn the particle with.
The index used is between index and index+rand. The index used is between index and index+rand.
@ -279,6 +283,10 @@ emitstart <seconds>
spawnorg <horiz> [vert] spawnorg <horiz> [vert]
spawnvel <horiz> [vert] spawnvel <horiz> [vert]
viewspace [frac]
Specifies that this particle type should move relative to the camera.
Not compatible with splitscreen.
perframe perframe
apply inverse frametime to count (causes emits to be per frame) apply inverse frametime to count (causes emits to be per frame)