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 "particles.h"
#include "shader.h"
#include "glquake.h"
extern cvar_t cl_predict_players;
extern cvar_t cl_predict_players_frac;
@ -100,8 +101,13 @@ void CL_FreeDlights(void)
int i;
if (cl_dlights)
for (i = 0; i < rtlights_max; i++)
{
if (cl_dlights[i].worldshadowmesh)
SH_FreeShadowMesh(cl_dlights[i].worldshadowmesh);
if (cl_dlights[i].coronaocclusionquery)
qglDeleteQueriesARB(1, &cl_dlights[i].coronaocclusionquery);
}
#endif
rtlights_max = cl_maxdlights = 0;
@ -110,6 +116,7 @@ void CL_FreeDlights(void)
}
void CL_InitDlights(void)
{
CL_FreeDlights();
rtlights_max = cl_maxdlights = RTL_FIRST;
cl_dlights = BZ_Realloc(cl_dlights, 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)
{
void *sm;
sm = dl->worldshadowmesh;
void *sm = dl->worldshadowmesh;
unsigned int oq = dl->coronaocclusionquery;
unsigned int oqr = (dl->key == key)?dl->coronaocclusionresult:false;
memset (dl, 0, sizeof(*dl));
dl->coronaocclusionquery = oq;
dl->coronaocclusionresult = oqr;
dl->rebuildcache = true;
dl->worldshadowmesh = sm;
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;
}
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)
{
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)
@ -2290,7 +2294,7 @@ void CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qbo
{
// int oldents = cl_numvisedicts;
// cl_numvisedicts = 0;
BE_DrawWorld(false, NULL);
BE_DrawWorld(NULL, NULL);
cl_numstris = 0;
// cl_numvisedicts = oldents;
}
@ -2938,7 +2942,7 @@ void CL_LinkStaticEntities(void *pvs)
model_t *clmodel;
extern cvar_t r_drawflame, gl_part_flame;
if (r_drawflame.ival < 0)
if (r_drawflame.ival < 0 || r_drawentities.ival == 0)
return;
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_c2sdupe = CVAR("cl_c2sdupe", "0");
cvar_t cl_c2spps = CVAR("cl_c2spps", "0");
cvar_t cl_c2sImpulseBackup = SCVAR("cl_c2sImpulseBackup","3");
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)
}
//its not time yet
if (time < ceil(1000 / fps))
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;
@ -1320,10 +1327,6 @@ int CL_IndepPhysicsThread(void *param)
spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, false);
if (spare)
{
//don't let them bank too much and get sudden bursts
if (spare > 15)
spare = 15;
time -= spare/1000.0f;
Sys_LockMutex(indeplock);
if (cls.state)
@ -1529,6 +1532,7 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)
cmd->lightlevel = 0;
#ifdef CSQC_DAT
if (!runningindepphys)
CSQC_Input_Frame(plnum, cmd);
#endif
memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum]));
@ -1631,8 +1635,10 @@ void CL_SendCmd (double frametime, qboolean mainloop)
static float pps_balance = 0;
static int dropcount = 0;
static double msecs;
static double msecsround;
int msecstouse;
qboolean dontdrop=false;
float usetime;
clcmdbuf_t *next;
@ -1739,39 +1745,28 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#ifdef IRCCONNECT
if (cls.netchan.remote_address.type != NA_IRC)
#endif
if (msecs>150) //q2 has 200 slop.
msecs=150;
msecs += frametime*1000;
// Con_Printf("%f\n", msecs);
if (msecs<0)
msecs=0; //erm.
msecstouse = (int)msecs; //casts round down.
if (msecstouse == 0)
return;
#ifdef IRCCONNECT
if (cls.netchan.remote_address.type != NA_IRC)
#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;
fullsend = true;
msecstouse = 0;
#ifndef CLIENTONLY
if (sv.state && cls.state != ca_active)
{
fullsend = -1;
msecstouse = usetime = msecs;
msecs = 0;
}
else
#endif
if (!runningindepphys)
{
// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up
if (cls.state < ca_active && !cls.download)
@ -1783,22 +1778,41 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#endif
wantfps = 12.5;
}
if (cl_netfps.value > 0 || !fullsend)
if (!runningindepphys && (cl_netfps.value > 0 || !fullsend))
{
float spare;
spare = CL_FilterTime(msecstouse, wantfps, false);
if (!spare && (msecstouse < 200
#ifdef IRCCONNECT
|| cls.netchan.remote_address.type == NA_IRC
#endif
))
spare = CL_FilterTime(msecs, wantfps, false);
usetime = msecsround + (msecs - spare);
msecstouse = (int)usetime;
if (!spare)
fullsend = false;
if (spare > cl_sparemsec.ival)
spare = cl_sparemsec.ival;
if (spare > 0)
msecstouse -= spare;
else
{
msecsround = usetime - msecstouse;
msecs = spare + msecstouse;
}
}
else
{
usetime = msecsround + msecs;
msecstouse = (int)usetime;
msecsround = usetime - msecstouse;
}
}
if (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times
msecstouse = 200;
// align msecstouse to avoid servers wasting our msecs
if (msecstouse > 100)
msecstouse &= ~3; // align to 4
else if (msecstouse > 50)
msecstouse &= ~1; // align to 2
if (msecstouse < 0) //FIXME
fullsend = false;
if (usetime <= 0)
return; //infinite frame times = weirdness.
#ifdef HLCLIENT
if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0]))
@ -1829,7 +1843,12 @@ void CL_SendCmd (double frametime, qboolean mainloop)
// if (cl.spectator)
Cam_Track(&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
@ -1839,9 +1858,12 @@ void CL_SendCmd (double frametime, qboolean mainloop)
// if (skipcmd)
// return;
if (!fullsend || !msecstouse)
if (!fullsend)
return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems
if (msecstouse == 12)
msecstouse = 13;
// if (msecstouse > 127)
// Con_Printf("%i\n", msecstouse, msecs);
@ -2003,6 +2025,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
//
// deliver the message
//
cls.netchan.dupe = cl_c2sdupe.ival;
Netchan_Transmit (&cls.netchan, buf.cursize, buf.data, 2500);
if (cls.netchan.fatal_error)
@ -2051,6 +2074,7 @@ void CL_InitInput (void)
Cvar_Register (&prox_inmenu, inputnetworkcvargroup);
Cvar_Register (&cl_c2sdupe, inputnetworkcvargroup);
Cvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup);
Cvar_Register (&cl_c2spps, 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!
spare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver);
if (!spare)
double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver);
if (!newspare)
{
while(COM_DoWork(0, false))
;
@ -4872,6 +4872,7 @@ double Host_Frame (double time)
spare = 0; //uncapped.
if (spare > cl_sparemsec.ival)
spare = cl_sparemsec.ival;
spare = newspare;
// realtime -= spare/1000; //don't use it all!
}
@ -4889,7 +4890,7 @@ double Host_Frame (double time)
CL_ProgressDemoTime();
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();

View file

@ -5835,7 +5835,7 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n
{
flocation_t loc;
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));
}
#ifdef PLUGINS
@ -6568,13 +6568,13 @@ void CLQW_ParseServerMessage (void)
case svcfte_cgamepacket:
csqcpacket = true;
#ifdef CSQC_DAT
if (CSQC_ParseGamePacket())
break;
#endif
#ifdef HLCLIENT
if (CLHL_ParseGamePacket())
break;
#endif
#ifdef CSQC_DAT
if (CSQC_ParseGamePacket())
break;
#endif
Con_Printf("Unable to parse gamecode packet\n");
break;

View file

@ -1246,7 +1246,6 @@ void CL_PredictMovePNum (int seat)
{
for (i=0 ; i<3 ; i++)
{
extern cvar_t temp1;
pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[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)
{
//easiest way to handle these
//should probably also do qwgunshot ones with nq protocols or something
switch(type)
{
case TENQ_EXPLOSION2:
@ -1107,10 +1108,41 @@ void CL_ParseTEnt (void)
case TE_EXPLOSION:
type = TEQW_EXPLOSIONNOSPRITE;
break;
case TE_GUNSHOT:
type = TE_GUNSHOT_NQCOMPAT;
break;
case TE_GUNSHOT_NQCOMPAT:
type = TE_GUNSHOT;
break;
default:
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)
{
case TE_WIZSPIKE: // spike hitting wall
@ -1509,8 +1541,10 @@ void CL_ParseTEnt (void)
P_RunParticleEffect (pos, vec3_origin, 0, 20);
break;
case TE_GUNSHOT: // bullet hitting wall
if (nqprot)
case TE_GUNSHOT_NQCOMPAT:
if (type == TE_GUNSHOT_NQCOMPAT)
cnt = 1;
else
cnt = MSG_ReadByte ();
@ -3301,7 +3335,7 @@ fixme:
ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
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");
break;
case CRTE_BLUE_MUZZLEFLASH:
@ -3309,7 +3343,7 @@ fixme:
ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
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");
break;
case CRTE_SMART_MUZZLEFLASH:
@ -3317,7 +3351,7 @@ fixme:
ex = CL_AllocExplosion (pos);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
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");
break;
case CRTE_LEADERFIELD:
@ -3328,7 +3362,7 @@ fixme:
VectorCopy (pos, ex->origin);
ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW;
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");
break;
case CRTE_BLASTERBEAM:

View file

@ -309,8 +309,8 @@ typedef struct
void (QDECL *player_setkey) (char *key, char *value); //wait, no pnum?
qboolean (QDECL *getcdkey) (int playernum, char key[16]);
int trackerfromplayer;
int playerfromtracker;
int (QDECL *trackerfromplayer) (int pl);
int (QDECL *playerfromtracker) (int tr);
int (QDECL *sendcmd_unreliable) (char *cmd);
void (QDECL *getsysmousepos) (long *xandy);
void (QDECL *setsysmousepos) (int x, int y);
@ -707,8 +707,8 @@ void QDECL CLGHL_getplayerinfo (int entnum, hlplayerinfo_t *result)
result->isus = true;
result->isspec = player->spectator;
result->pl = player->pl;
if (player->skin)
result->model = player->skin->name;
if (player->qwskin)
result->model = player->qwskin->name;
else
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);
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)
{
@ -733,7 +733,7 @@ void QDECL CLGHL_startsound_idx (int idx, float vol)
Con_Printf ("CLGHL_startsound_name: index not precached %s\n", name);
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)
@ -1175,8 +1175,8 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =
CLGHL_player_setkey,
CLGHL_getcdkey,
(void*)0xdeaddead,//CLGHL_trackerfromplayer;
(void*)0xdeaddead,//CLGHL_playerfromtracker;
CLGHL_trackerfromplayer,
CLGHL_playerfromtracker,
CLGHL_sendcmd_unreliable,
CLGHL_getsysmousepos,
CLGHL_setsysmousepos,
@ -1186,7 +1186,7 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =
0xdeadbeef
};
dllhandle_t clg;
dllhandle_t *clg;
int CLHL_GamecodeDoesMouse(void)
{
@ -1270,7 +1270,8 @@ void CLHL_LoadClientGame(void)
clg = 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");
clg = Sys_LoadLibrary(fullname, funcs);
@ -1308,6 +1309,20 @@ void CLHL_LoadClientGame(void)
CLHL_cgamefuncs.HUD_Init();
if (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)
@ -1329,7 +1344,7 @@ int CLHL_BuildUserInput(int msecs, usercmd_t *cmd)
cmd->upmove = hlcmd.upmove;
cmd->weapon = hlcmd.weaponselect;
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;
return true;
#else
@ -1365,7 +1380,7 @@ int CLHL_DrawHud(void)
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;
}
@ -1383,7 +1398,7 @@ int CLHL_AnimateViewEntity(entity_t *ent)
return true;
}
explosion_t *CL_AllocExplosion (void);
explosion_t *CL_AllocExplosion (vec3_t org);
int CLHL_ParseGamePacket(void)
{
@ -1445,19 +1460,18 @@ int CLHL_ParseGamePacket(void)
if (!(flags & 8))
P_RunParticleEffectType(startp, NULL, 1, pt_explosion);
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))
CL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0);
ef = CL_AllocExplosion();
VectorCopy(startp, ef->origin);
ef = CL_AllocExplosion(startp);
ef->start = cl.time;
ef->model = cl.model_precache[midx];
ef->framerate = mrate;
ef->firstframe = 0;
ef->numframes = ef->model->numframes;
if (!(flags & 1))
ef->flags = Q2RF_ADDITIVE;
ef->flags = RF_ADDITIVE;
else
ef->flags = 0;
break;
@ -1503,8 +1517,7 @@ int CLHL_ParseGamePacket(void)
MSG_ReadByte();
lifetime = MSG_ReadByte();
ef = CL_AllocExplosion();
VectorCopy(startp, ef->origin);
ef = CL_AllocExplosion(startp);
ef->start = cl.time;
ef->angles[1] = ang;
ef->model = cl.model_precache[midx];
@ -1554,7 +1567,7 @@ int CLHL_ParseGamePacket(void)
break;
case svc_intermission:
//nothing.
cl.intermission = true;
cl.intermissionmode = IM_NQSCORES;
break;
case svc_cdtrack:
{
@ -1572,7 +1585,7 @@ int CLHL_ParseGamePacket(void)
break;
case 37: //svc_roomtype
tempi = MSG_ReadShort();
S_SetUnderWater(tempi==14||tempi==15||tempi==16);
// S_SetUnderWater(tempi==14||tempi==15||tempi==16);
break;
default:
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;
char cubemapname[64];
int coronaocclusionquery;
unsigned int coronaocclusionresult;
//the following are used for rendering (client code should clear on create)
qboolean rebuildcache;
struct shadowmesh_s *worldshadowmesh;
@ -975,7 +978,6 @@ void CL_FreeDlights(void);
dlight_t *CL_AllocDlight (int key);
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_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);
void CL_DecayLights (void);

View file

@ -971,6 +971,17 @@ void VARGS Con_SafeTPrintf (translation_t text, ...)
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
@ -999,6 +1010,13 @@ void VARGS Con_DPrintf (const char *fmt, ...)
vsnprintf (msg,sizeof(msg)-1, fmt,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)
Con_Log(msg);
else

View file

@ -28,7 +28,9 @@ char *r_defaultimageextensions =
#if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB)
" jpg" //q3 uses some jpegs, for some reason
#endif
#ifndef NOLEGACY
" 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);
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, "%s/%s%s", 1}, /*fuhquake compatibility*/
{2, "textures/%s%s", 1}, /*directly named texture with textures/ prefix*/
#ifndef NOLEGACY
{2, "override/%s%s", 1} /*tenebrae compatibility*/
#endif
};
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;
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:
if (!palettedata)
{
@ -4458,6 +4510,8 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned
case TF_BGRA32:
b *= 4;
break;
case TF_MIP4_8PAL24:
pb = 3*256;
case TF_MIP4_LUM8:
case TF_MIP4_SOLID8:
b = (fallbackwidth>>0)*(fallbackheight>>0) +
@ -4508,9 +4562,9 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned
else
#endif
if (lowpri)
COM_AddWork(5, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
else
COM_AddWork(2+(seq++%3), Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
}
return tex;
}
@ -4711,6 +4765,7 @@ void Image_List_f(void)
//may not create any images yet.
void Image_Init(void)
{
wadmutex = Sys_CreateMutex();
memset(imagetablebuckets, 0, sizeof(imagetablebuckets));
Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets);
@ -4736,6 +4791,10 @@ void Image_Shutdown(void)
}
if (i)
Con_DPrintf("Destroyed %i/%i images\n", j, i);
if (wadmutex)
Sys_DestroyMutex(wadmutex);
wadmutex = NULL;
}
//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;
qboolean strafe_x, strafe_y;
int wpnum;
extern qboolean runningindepphys;
//small performance boost
if (mouse->type == M_INVALID)
@ -570,7 +571,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum)
#ifdef PEXT_CSQC
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;
my = 0;
@ -579,7 +580,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum)
else
{
if (mx || my)
if (CSQC_MouseMove(mx, my, mouse->qdeviceid))
if (!runningindepphys && CSQC_MouseMove(mx, my, mouse->qdeviceid))
{
mx = 0;
my = 0;

View file

@ -1651,7 +1651,7 @@ cin_t *Media_WinAvi_TryLoad(char *name)
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))
{
@ -4424,6 +4424,15 @@ static void S_MP3_Purge(sfx_t *sfx)
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*/
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.purge = S_MP3_Purge;
s->decoder.decodedata = S_MP3_Locate;
s->decoder.querydata = S_MP3_Query;
dec->dstdata = NULL;
dec->dstcount = 0;

View file

@ -190,6 +190,7 @@ typedef enum uploadfmt
TF_LUM8, /*8bit greyscale image*/
TF_MIP4_LUM8, /*8bit 4-mip greyscale image*/
TF_MIP4_SOLID8, /*8bit 4-mip image*/
TF_MIP4_8PAL24, /*8bit 4-mip image*/
TF_SOLID8, /*8bit quake-palette image*/
TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/
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);
struct batch_s *(*BE_GetTempBatch)(void);
//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
void (*BE_Init)(void);
//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 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
float viewspacefrac;
float s1, t1, s2, t2; //texture coords
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_TRUNDERWATER 0x0400 // don't spawn if overwater
#define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows.
unsigned int fluidmask;
unsigned int state;
#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->cliptype = P_INVALID;
ptype->emit = P_INVALID;
ptype->fluidmask = FTECONTENTS_FLUID;
ptype->alpha = 1;
ptype->alphachange = 1;
ptype->clipbounce = 0.8;
@ -1243,7 +1246,7 @@ void P_ParticleEffect_f(void)
ptype->alpha = atof(value);
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);
}
else if (!strcmp(var, "alphadelta"))
@ -1268,7 +1271,7 @@ void P_ParticleEffect_f(void)
}
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);
}
@ -1339,6 +1342,49 @@ void P_ParticleEffect_f(void)
ptype = &part_type[pnum];
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"))
{
partmodels_t *mod;
@ -1623,7 +1669,7 @@ void P_ParticleEffect_f(void)
}
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;
}
else if (!strcmp(var, "spawntime"))
@ -1662,22 +1708,22 @@ void P_ParticleEffect_f(void)
// old names
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);
}
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);
}
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);
}
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);
}
@ -1713,7 +1759,7 @@ void P_ParticleEffect_f(void)
ptype->rampmode = RAMP_NONE;
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;
}
else if (!strcmp(value, "nearest"))
@ -1805,6 +1851,8 @@ void P_ParticleEffect_f(void)
ptype->rampindexes++;
}
else if (!strcmp(var, "viewspace"))
ptype->viewspacefrac = (Cmd_Argc()>1)?atof(value):1;
else if (!strcmp(var, "perframe"))
ptype->flags |= PT_INVFRAMETIME;
else if (!strcmp(var, "averageout"))
@ -1819,7 +1867,7 @@ void P_ParticleEffect_f(void)
else if (!strcmp(var, "lightradius"))
{ //float version
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] -= ptype->dl_radius[0];
}
@ -1862,7 +1910,7 @@ void P_ParticleEffect_f(void)
ptype->stain_rgb[2] = atof(Cmd_Argv(4));
}
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->loaded = part_parseweak?1:2;
@ -1883,12 +1931,12 @@ void P_ParticleEffect_f(void)
if (ptype->scale)
{
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
{
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)
@ -1924,11 +1972,11 @@ void P_ParticleEffect_f(void)
if (ptype->rampmode && !ptype->ramp)
{
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)
{
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);
@ -2108,6 +2156,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen)
if (ptype->areaspread || ptype->areaspreadvert)
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->randomveladd)
@ -3920,9 +3971,9 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
int cont;
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;
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID))
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
goto skip;
}
@ -4724,9 +4775,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype
int cont;
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;
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID))
if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
return;
}
@ -5853,6 +5904,8 @@ static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)
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 (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle;
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;
{
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)
{
if (type->clippeddecals)
@ -6254,6 +6315,15 @@ static void PScript_DrawParticleTypes (void)
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;
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);
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)
G_FLOAT(OFS_RETURN) = 0;
else
{
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);
else
cache = sfx->decoder.buf;
if (!cache)
G_FLOAT(OFS_RETURN) = 0;
else
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;
Surf_NewMap();
CL_UpdateWindowTitle();
World_RBE_Shutdown(&csqc_world);
World_RBE_Start(&csqc_world);
}
if (cl.worldmodel)

View file

@ -2327,6 +2327,15 @@ pbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc)
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;
qboolean MP_Init (void)
{
@ -2350,7 +2359,7 @@ qboolean MP_Init (void)
menuprogparms.progsversion = PROGSTRUCT_VERSION;
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.Printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...);
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);
#define LMBLOCK_SIZE_MAX 2048 //single axis
typedef struct glRect_s {
unsigned short l,t,w,h;
unsigned short l,t,r,b;
} glRect_t;
typedef unsigned char stmap;
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 R_PushDlights (void);
qbyte *R_MarkLeaves_Q1 (void);
qbyte *R_MarkLeaves_Q1 (qboolean getvisonly);
qbyte *R_CalcVis_Q1 (void);
qbyte *R_MarkLeaves_Q2 (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_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_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_ARCHIVE);
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_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_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.");
@ -2209,7 +2209,7 @@ qbyte *R_CalcVis_Q1 (void)
}
#endif
qbyte *R_MarkLeaves_Q1 (void)
qbyte *R_MarkLeaves_Q1 (qboolean getvisonly)
{
static qbyte *cvis[R_MAX_RECURSE];
qbyte *vis;
@ -2271,7 +2271,9 @@ qbyte *R_MarkLeaves_Q1 (void)
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.
//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++)
{
if (cl.spectator)
if (cl.spectator && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV))
{
int t = cl.playerview[pnum].cam_spec_track;
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)
{
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;
}
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);
}
#endif
memset(&sh_config, 0, sizeof(sh_config));
return true;
}
static void Headless_VID_DeInit (void)
@ -196,7 +192,7 @@ static struct batch_s *Headless_BE_GetTempBatch (void)
{
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)

View file

@ -244,6 +244,14 @@ void SwapPic (qpic_t *pic)
//FIXME: convert to linked list. is hunk possible?
//hash tables?
#define TEXWAD_MAXIMAGES 16384
typedef struct wadfile_s
{
vfsfile_t *file;
struct wadfile_s *next;
char name[1];
} wadfile_t;
typedef struct
{
char name[16];
@ -253,18 +261,15 @@ typedef struct
} texwadlump_t;
int numwadtextures;
static texwadlump_t texwadlump[TEXWAD_MAXIMAGES];
typedef struct wadfile_s {
vfsfile_t *file;
struct wadfile_s *next;
char name[1];
} wadfile_t;
void *wadmutex;
wadfile_t *openwadfiles;
void Wads_Flush (void)
{
wadfile_t *wf;
if (wadmutex)
Sys_LockMutex(wadmutex);
while(openwadfiles)
{
VFS_CLOSE(openwadfiles->file);
@ -275,6 +280,8 @@ void Wads_Flush (void)
}
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[2] == tex->offsets[1] + (tex->width>>1)*(tex->height>>1) &&
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;
else
pal = host_basepal;
if (tex->offsets[0] + tex->width * tex->height > lumpsize)
{ //fucked texture.
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++;
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;
W_CleanupName (name, texname);
Sys_LockMutex(wadmutex);
for (i = 0;i < numwadtextures;i++)
{
if (!strcmp(texname, texwadlump[i].name)) // found it
{
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
if (!tex)
return NULL;
if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size)
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;}
if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)
{
Sys_UnlockMutex(wadmutex);
tex->width = LittleLong(tex->width);
tex->height = LittleLong(tex->height);
for (j = 0;j < MIPLEVELS;j++)
@ -497,6 +514,11 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp
return data;
}
}
Con_Printf("W_GetTexture: corrupt WAD3 file\n");
break;
}
}
Sys_UnlockMutex(wadmutex);
return NULL;
}
@ -509,20 +531,18 @@ miptex_t *W_GetMipTex(const char *name)
texname[16] = 0;
W_CleanupName (name, texname);
Sys_LockMutex(wadmutex);
for (i = 0;i < numwadtextures;i++)
{
if (!strcmp(texname, texwadlump[i].name)) // found it
{
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
if (!tex)
return NULL;
if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size)
{Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;}
if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)
{
Sys_UnlockMutex(wadmutex);
tex->width = LittleLong(tex->width);
tex->height = LittleLong(tex->height);
for (j = 0;j < MIPLEVELS;j++)
@ -530,6 +550,11 @@ miptex_t *W_GetMipTex(const char *name)
return tex;
}
}
Con_Printf("W_GetTexture: corrupt WAD3 file\n");
break;
}
}
Sys_UnlockMutex(wadmutex);
return NULL;
}
@ -689,12 +714,14 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
key[3] = ' ';
Q_strncpyz(key+4, token, sizeof(key)-4);
Cbuf_AddText(key, RESTRICT_INSECURE);
Cbuf_AddText("\n", RESTRICT_INSECURE);
}
else if (!strcmp("waterfog", key)) //q1 extension. FIXME: should be made temporary.
{
memcpy(key, "waterfog ", 9);
Q_strncpyz(key+9, token, sizeof(key)-9);
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).
{

View file

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

View file

@ -3546,7 +3546,7 @@ qbool TP_CheckSoundTrigger (char *str)
COM_DefaultExtension (soundname, ".wav", sizeof(soundname));
// 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;
// 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_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
#endif
#elif defined(MINIMAL)

View file

@ -616,7 +616,7 @@ void Cmd_Exec_f (void)
else
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);
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;
vec3_t dir, rel;
@ -2787,13 +2787,11 @@ void Mod_LoadAliasShaders(model_t *mod)
//Q1 model loading
#if 1
static galiasinfo_t *galias;
static dmdl_t *pq1inmodel;
#define NUMVERTEXNORMALS 162
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
// 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;
#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.
//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;
galiasanimation_t *frame = galias->ofsanimations;
@ -2910,12 +2908,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
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];
}
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];
}
@ -2972,12 +2970,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
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;
}
else
{
Alias_LoadPose(verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
pinframe += pq1inmodel->numverts;
}
@ -3007,7 +3005,7 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe
//greatly reduced version of Q1_LoadSkins
//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 s;
@ -3038,7 +3036,7 @@ static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintran
}
#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;
char skinname[MAX_QPATH];
@ -3362,8 +3360,8 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
dh2triangle_t *pinh2triangles;
qboolean rapo = false;
#endif
pq1inmodel = (dmdl_t *)buffer;
galiasinfo_t *galias;
dmdl_t *pq1inmodel = (dmdl_t *)buffer;
hdrsize = sizeof(dmdl_t) - sizeof(int);
@ -3450,11 +3448,11 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
{
default:
#ifndef SERVERONLY
pinstverts = (dstvert_t *)Q1_LoadSkins_GL(mod, skinstart, skintranstype);
pinstverts = (dstvert_t *)Q1MDL_LoadSkins_GL(galias, pq1inmodel, mod, skinstart, skintranstype);
break;
#endif
case QR_NONE:
pinstverts = (dstvert_t *)Q1_LoadSkins_SV(skinstart, skintranstype);
pinstverts = (dstvert_t *)Q1MDL_LoadSkins_SV(galias, pq1inmodel, skinstart, skintranstype);
break;
}
@ -3535,7 +3533,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
#endif
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);
ZG_FreeGroup(&mod->memgroup);
@ -3612,7 +3610,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
end = &pinq1triangles[pq1inmodel->numtris];
//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);
ZG_FreeGroup(&mod->memgroup);
@ -3706,7 +3704,7 @@ typedef struct
#define Q2NUMVERTEXNORMALS 162
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
int i;
@ -3777,6 +3775,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize)
int numverts;
int size;
galiasinfo_t *galias;
mod->engineflags |= MDLF_NEEDOVERBRIGHT;
@ -3820,7 +3819,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize)
galias->nextsurf = 0;
//skins
Q2_LoadSkins(mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins)));
Q2MD2_LoadSkins(galias, mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins)));
//trianglelists;
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;
md3Surface_t *surf;
galiasinfo_t *galias;
header = buffer;

View file

@ -4724,11 +4724,24 @@ void COM_ErrorMe_f(void)
#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*/
static void *com_workercondition[WORKERTHREADS];
static qboolean com_workerdone[WORKERTHREADS];
static void *com_workerthread[WORKERTHREADS];
static int com_liveworkers[WG_COUNT];
static void *com_workercondition[WG_COUNT];
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;
qboolean com_workererror;
static struct com_work_s
@ -4739,77 +4752,12 @@ static struct com_work_s
void *data;
size_t a;
size_t b;
} *com_work_head[WORKERTHREADS], *com_work_tail[WORKERTHREADS];
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();
}
} *com_work_head[WG_COUNT], *com_work_tail[WG_COUNT];
//return if there's *any* loading that needs to be done anywhere.
qboolean COM_HasWork(void)
{
unsigned int i;
for (i = 0; i < WORKERTHREADS-1; i++)
for (i = 0; i < WG_COUNT; i++)
{
if (com_work_head[i])
return true;
@ -4817,12 +4765,15 @@ qboolean COM_HasWork(void)
return false;
}
//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;
if (tg >= WG_COUNT)
return;
//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);
return;
@ -4837,19 +4788,19 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t
work->b = b;
//queue it (fifo)
Sys_LockConditional(com_workercondition[thread]);
if (com_work_tail[thread])
Sys_LockConditional(com_workercondition[tg]);
if (com_work_tail[tg])
{
com_work_tail[thread]->next = work;
com_work_tail[thread] = work;
com_work_tail[tg]->next = work;
com_work_tail[tg] = work;
}
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_ConditionSignal(com_workercondition[thread]);
Sys_UnlockConditional(com_workercondition[thread]);
Sys_ConditionSignal(com_workercondition[tg]);
Sys_UnlockConditional(com_workercondition[tg]);
// if (!com_workerthread[thread])
// 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 = true == safe sleeping
qboolean COM_DoWork(int thread, qboolean leavelocked)
qboolean COM_DoWork(int tg, qboolean leavelocked)
{
struct com_work_s *work;
if (tg >= WG_COUNT)
return false;
if (!leavelocked)
{
//skip the locks if it looks like we can be lazy.
if (!com_work_head[thread])
if (!com_work_head[tg])
return false;
Sys_LockConditional(com_workercondition[thread]);
Sys_LockConditional(com_workercondition[tg]);
}
work = com_work_head[thread];
work = com_work_head[tg];
if (work)
com_work_head[thread] = work->next;
if (!com_work_head[thread])
com_work_head[thread] = com_work_tail[thread] = NULL;
com_work_head[tg] = work->next;
if (!com_work_head[tg])
com_work_head[tg] = com_work_tail[tg] = NULL;
if (work)
{
// 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);
Z_Free(work);
if (leavelocked)
Sys_LockConditional(com_workercondition[thread]);
Sys_LockConditional(com_workercondition[tg]);
return true; //did something, check again
}
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.
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;
}
}
*(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)
/*static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b)
{
Sys_LockConditional(com_workercondition[a]);
com_workerdone[a] = true;
Sys_ConditionSignal(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)
{
int thread = (void**)arg - com_workerthread;
Sys_LockConditional(com_workercondition[thread]);
do
struct com_worker_s *thread = arg;
int group = WG_LOADER;
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;
} while (Sys_ConditionWait(com_workercondition[thread]));
Sys_UnlockConditional(com_workercondition[thread]);
if (thread->request == WR_ACK)
{
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...
*(void**)arg = NULL;
//and wake up main thread
COM_WorkerSync_SignalMain(NULL, NULL, 0, 0);
//and wake up main thread to clean up our handle
COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, thread, NULL, 0, group);
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
void COM_AssertMainThread(const char *msg)
@ -4936,48 +4972,29 @@ void COM_AssertMainThread(const char *msg)
void COM_DestroyWorkerThread(void)
{
int i;
COM_WorkerFullSync();
if (!com_resourcemutex)
return;
// com_workererror = false;
Sys_LockConditional(com_workercondition[WG_LOADER]);
for (i = 0; i < WORKERTHREADS; i++)
{
if (com_workerthread[i])
{
void *thread = com_workerthread[i];
com_workerdone[0] = false;
//send it the terminate message
COM_AddWork(i, COM_WorkerSync_StopWorker, NULL, NULL, i, 0);
com_worker[i].request = WR_DIE; //flag them all to die
Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
//wait for the response while servicing anything that it might be waiting for.
Sys_LockConditional(com_workercondition[0]);
do
{
if (com_workererror)
break;
while(COM_DoWork(0, true))
while(COM_DoWork(WG_LOADER, false)) //finish any work that got posted to it that it neglected to finish.
;
if (com_workerdone[0])
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))
while(COM_DoWork(WG_MAIN, false))
;
}
}
for (i = 0; i < WORKERTHREADS; i++)
COM_WorkerFullSync();
for (i = 0; i < WG_COUNT; i++)
{
if (com_workercondition[i])
Sys_DestroyConditional(com_workercondition[i]);
com_workercondition[i] = NULL;
com_workerthread[i] = NULL;
}
if (com_resourcemutex)
Sys_DestroyMutex(com_resourcemutex);
com_resourcemutex = NULL;
}
@ -4988,36 +5005,56 @@ void COM_WorkerFullSync(void)
qboolean repeat;
int i;
for (i = 1; i < WORKERTHREADS; i++)
{
if (!com_workerthread[i])
continue;
while(COM_DoWork(WG_MAIN, false))
;
//main thread asks worker thread to set main thread's 'done' flag.
//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.
if (!com_liveworkers[WG_LOADER])
return;
com_workeracksequence++;
Sys_LockConditional(com_workercondition[WG_MAIN]);
do
{
int cmds = 0;
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_HasWork())
{
if (com_workererror)
break;
while(COM_DoWork(0, true))
cmds++;
if (com_workerdone[0])
break;
} while (Sys_ConditionWait(com_workercondition[0]));
Sys_UnlockConditional(com_workercondition[0]);
if (com_workererror)
break;
if (cmds > 1)
Sys_UnlockConditional(com_workercondition[WG_MAIN]);
Sys_LockConditional(com_workercondition[WG_LOADER]);
repeat = false;
for (i = 0; i < WORKERTHREADS; i++)
{
if (com_worker[i].ackseq != com_workeracksequence && com_worker[i].request == WR_NONE)
{
com_worker[i].request = WR_ACK;
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.
@ -5032,18 +5069,20 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
// 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.
//this avoids waiting for everything.
//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.
if (priorityctx)
{
unsigned int thread;
unsigned int grp;
qboolean found = false;
for (thread = 1; thread < WORKERTHREADS && !found; thread++)
for (grp = WG_LOADER; grp < WG_MAIN && !found; grp++)
{
Sys_LockConditional(com_workercondition[thread]);
for (link = &com_work_head[thread], work = NULL; *link; link = &(*link)->next)
Sys_LockConditional(com_workercondition[grp]);
for (link = &com_work_head[grp], work = NULL; *link; link = &(*link)->next)
{
prev = work;
work = *link;
@ -5052,30 +5091,30 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
*link = 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.
work->next = com_work_head[thread];
com_work_head[thread] = work;
work->next = com_work_head[grp];
com_work_head[grp] = work;
if (!work->next)
com_work_tail[thread] = work;
com_work_tail[grp] = work;
found = true;
break; //found it, nothing else to do.
}
}
//we've not actually added any work, so no need to signal
Sys_UnlockConditional(com_workercondition[thread]);
Sys_UnlockConditional(com_workercondition[grp]);
}
if (!found)
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
{
if (com_workererror)
break;
while(COM_DoWork(0, true))
while(COM_DoWork(WG_MAIN, true))
{
//give up as soon as we're done
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 (*address != value)
break;
} while (Sys_ConditionWait(com_workercondition[0]));
Sys_UnlockConditional(com_workercondition[0]);
} while (Sys_ConditionWait(com_workercondition[WG_MAIN]));
Sys_UnlockConditional(com_workercondition[WG_MAIN]);
// Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx);
}
@ -5094,7 +5133,7 @@ static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)
{
double *timestamp = data;
if (!b)
COM_AddWork(0, COM_WorkerPing, ctx, data, 0, 1);
COM_AddWork(WG_MAIN, COM_WorkerPing, ctx, data, 0, 1);
else
{
Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp);
@ -5102,35 +5141,66 @@ static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)
}
static void COM_WorkerTest_f(void)
{
if (com_workerthread)
{
double *timestamp = Z_Malloc(sizeof(*timestamp));
*timestamp = Sys_DoubleTime();
COM_AddWork(1, COM_WorkerPing, NULL, timestamp, 0, 0);
}
else
Con_Printf("Worker is not active.\n");
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;
cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling.");
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
{
//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);
}
}
}
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)
{
int i;
//in theory, we could run multiple workers, signalling a different one in turn for each bit of work.
com_resourcemutex = Sys_CreateMutex();
for (i = 0; i < WORKERTHREADS; i++)
for (i = 0; i < WG_COUNT; i++)
{
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++)
{
com_workerthread[i] = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_workerthread[i], 0, 256*1024);
}
worker_count.string = "0";
worker_count.flags |= CVAR_NOSET;
}
Cvar_Register(&worker_count, NULL);
Cvar_ForceCallback(&worker_count);
Cmd_AddCommand ("worker_test", COM_WorkerTest_f);
Cvar_Register(&worker_flush, NULL);

View file

@ -429,10 +429,16 @@ typedef struct {
} flocation_t;
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.
//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 flags, flocation_t *loc);
struct vfsfile_s *FS_OpenReadLocation(flocation_t *location);
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.
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_FCheckExists(filename) FS_FLocateFile(filename,FSLFRT_IFFOUND, 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,FSLF_IFFOUND, NULL)
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 len;
flocation_t loc;
len = FS_FLocateFile(path, FSLFRT_LENGTH, &loc);
return len;
if (FS_FLocateFile(path, FSLF_IFFOUND, &loc))
return loc.len;
else
return -1;
}
//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);
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");
if (FS_FLocateFile(f, FSLFRT_LENGTH, &loc)>=0)
if (FS_FLocateFile(f, FSLF_IFFOUND, &loc))
{
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.
//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;
searchpath_t *search;
@ -1026,7 +1027,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
goto fail;
}
if (com_fs_cache.ival && !com_fschanged)
if (com_fs_cache.ival && !com_fschanged && !(lflags & FSLF_IGNOREPURE))
{
pf = Hash_GetInsensitive(&filesystemhash, filename);
if (!pf)
@ -1035,17 +1036,19 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation
else
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.
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++;
found = search->handle->FindFile(search->handle, loc, filename, pf);
if (found)
{
if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH)
if (!(lflags & FSLF_DONTREFERENCE))
{
if ((search->flags & fs_referencetype) != fs_referencetype)
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.
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++;
found = search->handle->FindFile(search->handle, loc, filename, pf);
if (found)
{
if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH)
if (!(lflags & FSLF_DONTREFERENCE))
{
if ((search->flags & fs_referencetype) != fs_referencetype)
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:
if (found == FF_SYMLINK)
if (found == FF_SYMLINK && !(lflags & FSLF_IGNORELINKS))
{
static int blocklink;
if (blocklink < 4 && loc->len < MAX_QPATH)
@ -1125,7 +1130,7 @@ fail:
//and locate that instead.
blocklink++;
depth = FS_FLocateFile(mergedname, returntype, loc);
depth = FS_FLocateFile(mergedname, lflags, loc);
blocklink--;
if (!loc->search)
Con_Printf("Symlink %s -> %s (%s) is dead\n", filename, targname, mergedname);
@ -1143,20 +1148,14 @@ fail:
else
Con_Printf("Failed\n");
*/
if (returntype == FSLFRT_IFFOUND)
return (found != FF_NOTFOUND) && (loc->len != -1);
else if (returntype == FSLFRT_LENGTH)
{
if (found == FF_NOTFOUND)
return -1;
return loc->len;
}
else
if (lflags & (FSLF_DEPTH_EXPLICIT | FSLF_DEPTH_INEXPLICIT))
{
if (found == FF_NOTFOUND)
return 0x7fffffff;
return depth;
}
else
return (found != FF_NOTFOUND) && (loc->len != -1);
}
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;
}
FS_FLocateFile(filename, FSLFRT_IFFOUND, &loc);
FS_FLocateFile(filename, FSLF_IFFOUND, &loc);
if (loc.search)
{
@ -1856,9 +1855,8 @@ qbyte *COM_LoadFile (const char *path, int usehunk, size_t *filesize)
qbyte *buf;
qofs_t len;
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
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)
found = search->FindFile(search, &loc, name, NULL);
else
found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc);
found = FS_FLocateFile(name, FSLF_IFFOUND, &loc);
if (found)
{
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)
found = search->FindFile(search, &loc, name, NULL);
else
found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc);
found = FS_FLocateFile(name, FSLF_IFFOUND, &loc);
if (found)
{
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;
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);
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);
basechecksum ^= loc.search->crc_reply;
}
else Q_strncatz(buffer, va("%i ", 0), 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++)
{
FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); //q1
FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); //q1
vidpath[i] = loc.search?loc.search->handle:NULL;
}
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;
}
@ -4689,7 +4693,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
{
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))
{
vidrestart = true;
@ -4700,7 +4704,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
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))
{
reloadconfigs = true;

View file

@ -1365,7 +1365,12 @@ void Plug_Initialise(qboolean fromgamedir)
if (!numplugbuiltins)
{
Cvar_Register(&plug_sbar, "plugins");
#ifdef SUBSERVERS
if (!SSV_IsSubServer())
#endif
Cvar_Register(&plug_loaddefault, "plugins");
Cmd_AddCommand("plug_closeall", Plug_CloseAll_f);
Cmd_AddCommand("plug_close", Plug_Close_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;
flocation_t loc;
if (FS_FLocateFile(srcname, FSLFRT_IFFOUND, &loc))
if (FS_FLocateFile(srcname, FSLF_IFFOUND, &loc))
{
srcname = FS_WhichPackForLocation(&loc, makereferenced);
if (srcname == NULL)

View file

@ -844,7 +844,7 @@ enum clcq2_ops_e
enum {
TE_SPIKE = 0,
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_TAREXPLOSION = 4,
TE_LIGHTNING1 = 5,
@ -869,6 +869,7 @@ enum {
TEQW_BEAM = 18, //use the builtin, luke.
TEQW_EXPLOSION2 = 19, //use the builtin, luke.
TEQW_EXPLOSIONNOSPRITE = 20,
TE_GUNSHOT_NQCOMPAT = 21, //nq has count byte, qw does not
// hexen 2
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;
if (!handle)
return FS_FLocateFile(name, FSLFRT_IFFOUND, NULL);
return FS_FLocateFile(name, FSLF_IFFOUND, NULL);
*handle = 0;

View file

@ -354,23 +354,20 @@ qboolean Sys_ConditionWait(void *condv)
#endif
EnterCriticalSection(&cv->countlock);
done = cv->release > 0 && cv->waitgeneration != mygen;
LeaveCriticalSection(&cv->countlock);
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->release--;
done = cv->release == 0;
LeaveCriticalSection(&cv->countlock);
if (done)
ResetEvent(cv->evnt);
LeaveCriticalSection(&cv->countlock);
break;
}
LeaveCriticalSection(&cv->countlock);
}
EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition
return true;
}
@ -384,6 +381,7 @@ qboolean Sys_ConditionSignal(void *condv)
EnterCriticalSection(&cv->countlock);
if (cv->waiters > cv->release)
{
if (!cv->release)
SetEvent(cv->evnt);
cv->release++;
cv->waitgeneration++;
@ -405,6 +403,7 @@ qboolean Sys_ConditionBroadcast(void *condv)
EnterCriticalSection(&cv->countlock);
if (cv->waiters > 0)
{
if (!cv->release)
SetEvent(cv->evnt);
cv->release = cv->waiters;
cv->waitgeneration++;

View file

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

View file

@ -1264,7 +1264,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)
lightdir[2] = 1;
}
if (!r_vertexdlights.ival && r_dynamic.ival)
if (!r_vertexdlights.ival && r_dynamic.ival > 0)
{
float *org = e->origin;
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.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;
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 portaldepth = r_portalrecursion.ival;
for (i = start; i <= stop; i++)
{
if (drawworld)
if (worldbatches)
{
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)
return;
}
GLBE_SubmitMeshesSortList(model->batches[i]);
GLBE_SubmitMeshesSortList(worldbatches[i]);
}
GLBE_SubmitMeshesSortList(shaderstate.mbatches[i]);
}
@ -4941,6 +4940,8 @@ static void BE_UpdateLightmaps(void)
continue;
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;
if (!TEXVALID(lm->lightmap_texture))
{
@ -4957,14 +4958,14 @@ static void BE_UpdateLightmaps(void)
else
{
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, lm->rectchange.t,
lm->width, lm->rectchange.h, glformat, gltype,
lm->lightmaps+(lm->rectchange.t) *lm->width*lightmap_bytes);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t,
lm->width, b-t, glformat, gltype,
lm->lightmaps+t *lm->width*lightmap_bytes);
}
lm->rectchange.l = lm->width;
lm->rectchange.t = lm->height;
lm->rectchange.h = 0;
lm->rectchange.w = 0;
lm->rectchange.r = 0;
lm->rectchange.b = 0;
}
}
}
@ -4990,7 +4991,7 @@ void GLBE_BaseEntTextures(void)
batch_t **ob = shaderstate.mbatches;
shaderstate.mbatches = batches;
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);
shaderstate.mbatches = ob;
}
@ -5340,7 +5341,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
}
/*do portals*/
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);
if (!shaderstate.depthnormshader)
@ -5393,7 +5394,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
}
/*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*/
GLBE_FBO_Sources(shaderstate.tex_normals, r_nulltex);
@ -5405,7 +5406,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
GLBE_SelectEntity(&r_worldentity);
/*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*/
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)*/
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
/*regular lighting now*/
@ -5426,7 +5427,7 @@ void GLBE_DrawLightPrePass(qbyte *vis)
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;
batch_t *batches[SHADER_SORT_COUNT];
@ -5479,7 +5480,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
GLBE_SelectEntity(&r_worldentity);
BE_UpdateLightmaps();
if (drawworld)
if (worldbatches)
{
if (gl_overbright.modified)
{
@ -5493,7 +5494,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
}
#ifdef RTLIGHTS
if (drawworld && r_shadow_realtime_world.ival)
if (worldbatches && r_shadow_realtime_world.ival)
shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;
else
#endif
@ -5517,11 +5518,11 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
BE_SelectMode(BEM_STANDARD);
RSpeedRemark();
GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
RSpeedEnd(RSPEED_WORLD);
#ifdef RTLIGHTS
if (drawworld)
if (worldbatches)
{
RSpeedRemark();
TRACE(("GLBE_DrawWorld: drawing lights\n"));
@ -5535,7 +5536,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
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)
{
@ -5550,7 +5551,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
{
BE_SelectMode(BEM_WIREFRAME);
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);
qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
@ -5559,14 +5560,14 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis)
}
else
{
GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
#ifdef GL_LINE //no gles
if (r_wireframe.ival && qglPolygonMode)
{
BE_SelectMode(BEM_WIREFRAME);
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);
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;
if (FS_FLocateFile(fontfilename, FSLFRT_LENGTH, &loc)>0)
if (FS_FLocateFile(fontfilename, FSLF_IFFOUND, &loc))
{
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]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
}
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]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
}
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))
return false;
return FS_FLocateFile(name, FSLFRT_IFFOUND, loc);
return FS_FLocateFile(name, FSLF_IFFOUND, loc);
}
#endif
@ -2240,7 +2240,7 @@ static qboolean Terr_Collect(heightmap_t *hm)
#endif
/*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.
(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);
}
void Terr_FreeModel(model_t *mod)
{
heightmap_t *hm = mod->terrain;
@ -2354,6 +2355,7 @@ void Terr_FreeModel(model_t *mod)
mod->terrain = NULL;
}
}
#ifndef SERVERONLY
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 ((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.
//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.frac = 1;
hmtrace.contents = 0;
hmtrace.hitcontentsmask = against;
hmtrace.plane[0] = 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]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
lm = lightmap[s->lightmap]->lightmaps;
lm += ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap_bytes;
return lm;
@ -4449,8 +4452,8 @@ static void ted_dorelight(heightmap_t *hm)
lightmap[s->lightmap]->modified = true;
lightmap[s->lightmap]->rectchange.l = 0;
lightmap[s->lightmap]->rectchange.t = 0;
lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;
lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;
}
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->rectchange.l = 0;
lm->rectchange.t = 0;
lm->rectchange.w = lm->width;
lm->rectchange.h = lm->height;
lm->rectchange.r = lm->width;
lm->rectchange.b = lm->height;
in = br->faces[j].lightdata;
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);
if (!Brush_Deserialise(hm, &brush))
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);
}
else
@ -6608,7 +6626,7 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s
}
dist = DotProduct (best, in_normals[j]);
dist = in_distances[j] - dist;
if (dist < 0)
if (dist <= 0) //don't find coplanar brushes. add an epsilon if you need this.
break;
}
if (j == in_numplanes)
@ -6659,6 +6677,10 @@ void Terr_WriteMapFile(vfsfile_t *file, model_t *mod)
unsigned int entnum = 0;
heightmap_t *hm;
hm = mod->terrain;
if (hm && hm->exteriorcontents != FTECONTENTS_EMPTY)
VFS_WRITE(file, "terrain\n", 8);
start = entities;
while(entities)
{
@ -7278,13 +7300,36 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force)
return hm;
}
#ifndef SERVERONLY
void Mod_Terrain_Create_f(void)
{
int x,y;
hmsection_t *s;
heightmap_t *hm;
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));
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"
"classname \"worldspawn\"\n"
"message \"%s\"\n"
@ -7295,8 +7340,8 @@ void Mod_Terrain_Create_f(void)
"_minysegment -2048\n"
"_maxxsegment 2048\n"
"_maxysegment 2048\n"
"//_defaultgroundtexture city4_2\n"
"//_defaultwatertexture *water2\n"
"//_defaultgroundtexture \"city4_2\"\n"
"//_defaultwatertexture \"*water2\"\n"
"//_defaultgroundheight -1024\n"
"//_defaultwaterheight 0\n" //hurrah, sea level.
// "_tiles 64 64 8 8\n"
@ -7306,10 +7351,51 @@ void Mod_Terrain_Create_f(void)
"origin \"0 0 1024\"\n"
"}\n"
, 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.
//the new version will match our current format version.
//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_savever, "Terrain");
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);
#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.");
#endif

View file

@ -82,42 +82,16 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)
hlmdl_tex_t *tex;
hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls;
shader_t **shaders;
struct hlmodelshaders_s *shaders;
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
model = ZG_Malloc(&mod->memgroup, sizeof(hlmodelcache_t));
header = ZG_Malloc(&mod->memgroup, com_filesize);
memcpy(header, buffer, com_filesize);
header = ZG_Malloc(&mod->memgroup, fsize);
memcpy(header, buffer, fsize);
#if defined(HLSERVER) && (defined(__powerpc__) || defined(__ppc__))
//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;
if (!header->numtextures)
{
size_t fz;
char texmodelname[MAX_QPATH];
COM_StripExtension(mod->name, texmodelname, sizeof(texmodelname));
Q_strncatz(texmodelname, "t.mdl", sizeof(texmodelname));
//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->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);
*/
model->header = (char *)header - (char *)model;
model->texheader = (char *)texheader - (char *)model;
model->textures = (char *)tex - (char *)model;
model->bones = (char *)bones - (char *)model;
model->bonectls = (char *)bonectls - (char *)model;
model->header = header;
model->bones = bones;
model->bonectls = bonectls;
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++)
{
shaders[i] = R_RegisterSkin(va("%s_%i.tga", mod->name, i), mod->name);
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);
Q_snprintfz(shaders[i].name, sizeof(shaders[i].name), "%s_%i.tga", mod->name, i);
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;
@ -219,7 +196,7 @@ void *Mod_GetHalfLifeModelData(model_t *mod)
return NULL; //halflife models only, please
mc = Mod_Extradata(mod);
return (void*)((char*)mc + mc->header);
return (void*)mc->header;
}
#endif
@ -234,7 +211,7 @@ int HLMod_FrameForName(model_t *mod, const char *name)
mc = Mod_Extradata(mod);
h = (hlmdl_header_t *)((char *)mc + mc->header);
h = mc->header;
seqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex);
for (i = 0; i < h->numseq; i++)
@ -256,7 +233,7 @@ int HLMod_BoneForName(model_t *mod, char *name)
mc = Mod_Extradata(mod);
h = (hlmdl_header_t *)((char *)mc + mc->header);
h = mc->header;
bones = (hlmdl_bone_t*)((char*)h+h->boneindex);
for (i = 0; i < h->numbones; i++)
@ -278,7 +255,8 @@ int HLMod_BoneForName(model_t *mod, char *name)
void HL_CalculateBones
(
int offset,
int frame,
int frame1,
int frame2,
float lerpfrac,
vec4_t adjust,
hlmdl_bone_t *bone,
@ -290,7 +268,6 @@ void HL_CalculateBones
int i;
vec3_t angle;
float lerpifrac = 1-lerpfrac;
float t;
/*~~~~~~~~~~*/
/* For each vector */
@ -305,41 +282,30 @@ void HL_CalculateBones
if(animation->offset[o] != 0)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int tempframe = frame;
int tempframe;
hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]);
short f1, f2;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* find values including the required frame */
tempframe = frame1;
while(animvalue->num.total <= tempframe)
{
tempframe -= animvalue->num.total;
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))
{
//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];
}
tempframe -= animvalue->num.total;
animvalue += animvalue->num.valid + 1;
}
f2 = animvalue[min(animvalue->num.valid-1, tempframe)+1].value;
angle[i] += (f1 * lerpifrac + lerpfrac * f2) * bone->scale[o];
}
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 vec4_t quaternions[2], blended;
int frame;
int frame1, frame2;
hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +
((unsigned int)seqnum>=model->header->numseq?0:seqnum);
hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *)
((qbyte *) model->header + model->header->seqgroups) +
sequence->seqindex;
hlmdl_anim_t *animation = (hlmdl_anim_t *)
((qbyte *) model->header + sequencedata->data + sequence->index);
hlmdl_anim_t *animation;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
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;
if (frametime < 0)
frametime = 0;
frame = (int)frametime;
frametime -= frame;
frame1 = (int)frametime;
frametime -= frame1;
frame2 = frame1+1;
if (!sequence->numframes)
return;
if(frame >= sequence->numframes)
if(frame1 >= sequence->numframes)
{
if (sequence->loop)
frame %= sequence->numframes;
frame1 %= sequence->numframes;
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)
@ -489,10 +490,10 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl
subblendfrac = 1;
for(i = firstbone; i < lastbone; i++)
{
HL_CalculateBones(0, frame, 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(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[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);
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
* format.
*/
HL_CalculateBones(0, frame, 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(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[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);
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 vec3_t norm[2048];
static vec2_t st[2048];
static byte_vec4_t vc[2048];
static index_t index[4096];
int count;
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->tnormals_array = norm; //for rtlighting
mesh->indexes = index;
mesh->colors4b_array = vc;
for (b = 0; b < MAX_BONE_CONTROLLERS; 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][1] = order[3] * tex_t;
norm[vert][0] = 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;
vert++;
@ -673,12 +682,12 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches)
static mesh_t bmesh, *mptr = &bmesh;
//general model
model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header);
// model.texheader = (hlmdl_header_t *) ((char *)modelc + modelc->texheader);
model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures);
model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones);
model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls);
model.shaders = (shader_t **) ((char *)modelc + modelc->shaders);
model.header = modelc->header;
model.bones = modelc->bones;
model.bonectls = modelc->bonectls;
model.shaders = modelc->shaders;
model.animcache = modelc->animcache;
model.memgroup = &rent->model->memgroup;
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;
float tex_w;
float tex_h;
struct hlmodelshaders_s *s;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
if (mesh->skinindex >= modelc->numskins)
continue;
s = &model.shaders[modelc->skins[mesh->skinindex]];
if (batches)
{
shader_t *shader;
int sort;
int sort, j;
b = BE_GetTempBatch();
if (!b)
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->ent = rent;
b->mesh = NULL;
b->firstmesh = 0;
b->meshes = 1;
b->skin = &shader->defaulttextures;
b->skin = NULL;
b->texture = NULL;
b->shader = shader;
b->shader = s->shader;
for (j = 0; j < MAXRLIGHTMAPS; j++)
b->lightmap[j] = -1;
b->surf_first = batchid;
b->flags = 0;
sort = shader->sort;
sort = b->shader->sort;
//fixme: we probably need to force some blend modes based on the surface flags.
if (rent->flags & RF_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)
{
tex_w = 1.0f / model.textures[modelc->skins[mesh->skinindex]].w;
tex_h = 1.0f / model.textures[modelc->skins[mesh->skinindex]].h;
tex_w = 1.0f / s->w;
tex_h = 1.0f / s->h;
b->mesh = &mptr;
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;
#endif
qboolean isnotmap = true; //used to not warp ammo models.
void CM_Init(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
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 *shadername = tx->name;
if (!safetoloadfromwads)
{
/*skies? just replace with the override sky*/
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);
}
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))
R_InitSky (tx->shader, shadername, tx->mips[0], tx->width, tx->height);
else
@ -1559,10 +1568,10 @@ void Mod_NowLoadExternal(model_t *loadmodel)
if (!tx) //e1m2, this happens
continue;
if (tx->shader)
if (tx->mips[0])
continue;
Mod_FinishTexture(tx, loadname);
Mod_FinishTexture(tx, loadname, true);
}
#endif
}
@ -1661,11 +1670,12 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
if (!litdata && r_loadlits.value)
{
char *litnames[] = {
"maps/%s.lit2",
"maps/%s.lit",
"%s.lit2",
"%s.lit",
"lits/%s.lit2",
"lits/%s.lit"
};
char litbasep[MAX_QPATH];
char litbase[MAX_QPATH];
int depth;
int bestdepth = 0x7fffffff;
@ -1675,10 +1685,14 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
size_t litsize;
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++)
{
if (strchr(litnames[i], '/'))
Q_snprintfz(litname, sizeof(litname), litnames[i], litbase);
else
Q_snprintfz(litname, sizeof(litname), litnames[i], litbasep);
depth = COM_FDepthFile(litname, false);
if (depth < bestdepth)
{
@ -1688,7 +1702,10 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean
}
if (best >= 0)
{
if (strchr(litnames[best], '/'))
Q_snprintfz(litname, sizeof(litname), litnames[best], litbase);
else
Q_snprintfz(litname, sizeof(litname), litnames[best], litbasep);
litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize);
}
else
@ -2179,8 +2196,7 @@ qboolean Mod_LoadVertexNormals (model_t *loadmodel, qbyte *mod_base, lump_t *l)
Mod_LoadSubmodels
=================
*/
static qboolean hexen2map;
qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l)
qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map)
{
dq1model_t *inq;
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);
if (!inq->numfaces)
{
hexen2map = true;
*hexen2map = true;
if (l->filelen % sizeof(*inh))
{
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
{
hexen2map = false;
*hexen2map = false;
if (l->filelen % sizeof(*inq))
{
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 = sqrt(samps);
if (j > 128 || !r_dynamic.ival)
if (j > 128 || r_dynamic.ival <= 0)
samps *= 2;
mod->lightmaps.width = 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
=================
*/
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;
int i, j, count, p;
@ -3784,7 +3800,7 @@ void Mod_LoadCrouchHull(model_t *loadmodel)
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;
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))
Q_strncpyz(loadname, "bmodels", sizeof(loadname));
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);
@ -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.
//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;
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;
char loadname[32];
qbyte *mod_base = buffer;
qboolean hexen2map = false;
qboolean isnotmap;
#if (defined(ODE_STATIC) || defined(ODE_DYNAMIC))
qboolean ode = true;
#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]);
}
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)
{
TRACE(("Loading CH\n"));
@ -4689,11 +4707,11 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize)
TRACE(("Loading Vis\n"));
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"));
noerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header->lumps[LUMP_NODES], longm);
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)
{
TRACE(("Loading hull 0\n"));

View file

@ -137,8 +137,12 @@ typedef struct batch_s
#endif
/*caller-use, not interpreted by backend*/
union
{
struct
{
unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!)
unsigned int ebobatch; //
};
struct
{
unsigned int surf_first;
@ -952,6 +956,7 @@ typedef struct model_s
vbo_t *vbos;
void *terrain;
batch_t *batches[SHADER_SORT_COUNT];
unsigned int numbatches;
struct
{
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};
mesh_t flashblend_mesh;
mesh_t flashblend_fsmesh;
shader_t *occluded_shader;
shader_t *flashblend_shader;
shader_t *lpplight_shader;
@ -264,6 +265,15 @@ void R_InitFlashblends(void)
"}\n"
"}\n"
);
occluded_shader = R_RegisterShader("flashblend_occlusiontest", SUF_NONE,
"{\n"
"program defaultadditivesprite\n"
"{\n"
"maskcolor\n"
"maskalpha\n"
"}\n"
"}\n"
);
lpplight_shader = NULL;
}
@ -401,17 +411,98 @@ void R_RenderDlights (void)
/*coronas use depth testing to compute visibility*/
if (coronastyle)
{
extern cvar_t temp1;
if (r_coronas_occlusion.ival)
int method;
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))
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
{
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;
}
#endif
}
}
if (!R_BuildDlightMesh (l, intensity, cscale, coronastyle) && !coronastyle)
@ -530,7 +621,7 @@ void R_PushDlights (void)
return;
#endif
if (!r_dynamic.ival || !cl.worldmodel)
if (r_dynamic.ival <= 0|| !cl.worldmodel)
return;
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);
vbo->vertdata = NULL;
}
vbo->vertcount = vcount;
vbo->next = *vbochain;
*vbochain = vbo;
@ -506,6 +507,7 @@ void GLBE_GenBrushModelVBO(model_t *mod)
BZ_Free(vbo->vertdata);
vbo->vertdata = NULL;
}
vbo->vertcount = vcount;
}
#endif
}
@ -524,8 +526,8 @@ void GLBE_UploadAllLightmaps(void)
lm = lightmap[i];
lm->rectchange.l = lm->width;
lm->rectchange.t = lm->height;
lm->rectchange.w = 0;
lm->rectchange.h = 0;
lm->rectchange.r = 0;
lm->rectchange.b = 0;
if (!lm->modified)
continue;
lm->modified = false;

View file

@ -212,11 +212,11 @@ void GLSCR_UpdateScreen (void)
Media_RecordFrame();
#endif
RSpeedShow();
if (R2D_Flush)
R2D_Flush();
RSpeedEnd(RSPEED_TOTALREFRESH);
RSpeedShow();
RSpeedRemark();
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 |= IF_MIPCAP;
if (basefmt == TF_MIP4_SOLID8 && palette && palette != host_basepal)
basefmt = TF_MIP4_8PAL24;
COM_StripExtension(imagename, imagename, sizeof(imagename));
aframes = max(1, shader->numdefaulttextures);
@ -4710,8 +4713,8 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons
if (!TEXVALID(tex->fullbright))
{
int s=-1;
if (mipdata[0])
for(s = width*height; s-->0; )
if (mipdata[0] && (!palette || palette == host_basepal))
for(s = width*height-1; s>=0; s--)
{
if (mipdata[0][s] >= 256-vid.fullbright)
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);
}
void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop);
void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)
{
#ifdef GLQUAKE
@ -3221,7 +3219,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)
BE_SelectMode(BEM_CREPUSCULAR);
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);

View file

@ -62,6 +62,15 @@ void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid*
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
gl core uses different names/distinctions from the extension
*/
@ -1144,6 +1153,31 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
}
#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)
qglDisableClientState(GL_VERTEX_ARRAY);
}
@ -1822,7 +1856,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]);
strings++;
}
if (ver >= 140)
if (ver >= 130) //gl3+ deprecated the varying keyword for geometry shaders to work properly
{
prstrings[strings] =
"#define varying in\n"
@ -1862,7 +1896,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]);
strings++;
}
if (ver >= 140)
if (ver >= 130)
{
prstrings[strings] =
"#define attribute in\n"
@ -1871,7 +1905,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char *
length[strings] = strlen(prstrings[strings]);
strings++;
}
if (gl_config.nofixedfunc || ver >= 140)
if (gl_config.nofixedfunc || ver >= 130)
{
prstrings[strings] =
"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;
width /= 2;
if (width < 1 || height < 1 || stride != width*2)
if (width < 1 || height < 1 || stride != width*2 || !src)
return;
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 *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
extern FTEPFNGLCREATEPROGRAMOBJECTARBPROC qglCreateProgramObjectARB;
extern FTEPFNGLDELETEOBJECTARBPROC qglDeleteProgramObject_;

View file

@ -764,4 +764,14 @@ typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void);
#define GL_RGBA32F_ARB 0x8814
#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

View file

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

View file

@ -181,7 +181,7 @@ typedef struct
int unknown7[2];
float unknown[4];
int unknown8;
int seqindex;
unsigned int seqindex;
int unknown9[4];
} hlmdl_sequencelist_t;
@ -193,10 +193,17 @@ typedef struct
typedef struct
{
char name[96]; /* should be split label[32] and name[64] */
void * cache;
unsigned int cache;
int data;
} hlmdl_sequencedata_t;
typedef struct
{
int magic; //IDSQ
int version; //10
char name[64];
int unk1;
} hlmdl_sequencefile_t;
/*
-----------------------------------------------------------------------------------------------------------------------
halflife model internal structure
@ -209,21 +216,27 @@ typedef struct
/* Static pointers */
hlmdl_header_t *header;
hlmdl_header_t *texheader;
hlmdl_tex_t *textures;
hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls;
shader_t **shaders;
struct hlmodelshaders_s *shaders;
hlmdl_sequencefile_t **animcache;
zonegroup_t *memgroup;
} 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
{
int header;
int texheader;
int textures;
int bones;
int bonectls;
int shaders;
hlmdl_header_t *header;
hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls;
hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS];
struct hlmodelshaders_s
{
char name[MAX_QPATH];
texnums_t defaulttex;
shader_t *shader;
int w, h;
} *shaders;
short *skins;
int numskins;
} hlmodelcache_t;

View file

@ -717,12 +717,12 @@ batch_t *GLBE_GetTempBatch(void);
void GLBE_GenBrushModelVBO(model_t *mod);
void GLBE_ClearVBO(vbo_t *vbo);
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);
void GLBE_SelectEntity(entity_t *ent);
qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);
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_RenderToTextureUpdate2d(qboolean destchanged);
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_SetupViewCBuffer(void);
void D3D11_UploadLightmap(lightmapinfo_t *lm);
void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize);
void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray);
void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray);
void D3D11BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);
void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);
void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray);
void D3D11BE_VBO_Destroy(vboarray_t *vearray);
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);
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";
}

View file

@ -1651,6 +1651,14 @@ char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buf, int *bufo
#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)
{
#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);
}
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
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 ("\t{\n");
@ -1707,10 +1715,10 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufm
{
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
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)
arg+=2;

View file

@ -1072,17 +1072,17 @@ void PR_Compile_f(void)
argv[4] = Cmd_Argv(1);
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
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)
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
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);
/*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)
@ -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]));
/*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)
{
@ -3868,7 +3868,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean
else
{
/*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)
@ -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 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 enable F301 /*non-utf-8 strings*/\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;
}
if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc))
if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))
{
Con_Printf("Couldn't find %s.\n", name);
return false;
@ -1493,7 +1493,7 @@ void SV_Loadgame_f (void)
{
flocation_t loc;
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);
else if (!*loc.rawname || loc.offset)
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
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]);
sv.logindatabase = SQL_NewServer("sqlite", sqlparams);
@ -626,7 +629,7 @@ void SSV_ReadFromControlServer(void)
char *addr = MSG_ReadString();
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++)
{
@ -640,19 +643,19 @@ void SSV_ReadFromControlServer(void)
{
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));
}
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("cl_transfer \"%s\"\n", addr));
cl->redirect = 2;
}
}
else
Con_Printf("%s: tookplayer: invalid player.\n", sv.name);
Con_Printf("%s: tookplayer: invalid player.\n", sv.modelname);
}
break;
@ -723,7 +726,7 @@ void SSV_ReadFromControlServer(void)
}
else
{
Con_Printf("%s: server full!\n", sv.name);
Con_Printf("%s: server full!\n", sv.modelname);
}
j = MSG_ReadByte();
@ -838,7 +841,7 @@ void SSV_UpdateAddresses(void)
send.cursize = 2;
MSG_WriteByte(&send, ccmd_serveraddress);
MSG_WriteString(&send, sv.name);
MSG_WriteString(&send, sv.modelname);
for (i = 0; i < count; i++)
MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i]));
MSG_WriteByte(&send, 0);
@ -971,7 +974,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv
pubsubserver_t *s = NULL;
if (!s)
s = MSV_FindSubServerName(":start");
s = MSV_FindSubServerName(va(":%s", sv.modelname));
if (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s))
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)
MSG_WriteByte (msg, to->colormap);
if (bits & U_SKIN)
MSG_WriteByte (msg, to->skinnum);
MSG_WriteByte (msg, to->skinnum&0xff);
if (bits & U_EFFECTS)
MSG_WriteByte (msg, to->effects&0x00ff);
if (bits & U_ORIGIN1)
@ -3647,7 +3647,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
cameras = NULL;
#ifdef HLSERVER
else if (svs.gametype == GT_HALFLIFE)
pvs = SVHL_Snapshot_SetupPVS(client, pvsbuffer, sizeof(pvsbuffer));
SVHL_Snapshot_SetupPVS(client, cameras->pvs, sizeof(cameras->pvs));
#endif
else
SV_Snapshot_SetupPVS(client, cameras);
@ -3682,7 +3682,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
{
#ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE)
SVHL_Snapshot_Build(client, pack, pvs, clent, ignorepvs);
SVHL_Snapshot_Build(client, pack, cameras->pvs, clent, ignorepvs);
else
#endif
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 SVNQ_CreateBaseline (void)
void SVQ1_CreateBaseline (void)
{
edict_t *svent;
int entnum;
@ -1587,7 +1587,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
// create a baseline for more efficient communications
// SV_CreateBaseline ();
if (svprogfuncs)
SVNQ_CreateBaseline();
SVQ1_CreateBaseline();
#ifdef Q2SERVER
SVQ2_BuildBaselines();
#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.
if (svs.gametype == GT_HALFLIFE)
client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS;
client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS; //baseline issues
//
client->maxmodels = 256;
@ -2593,6 +2593,7 @@ client_t *SVC_DirectConnect(void)
Con_TPrintf ("%s:gamecode reject\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
return NULL;
}
temp.hledict = newcl->hledict;
}
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.
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");
if (strlen(val))

View file

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

View file

@ -1367,6 +1367,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
client->nextservertimeupdate = sv.physicstime;
*/
#ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE)
return;
#endif
#ifdef NQPROT
ent = client->edict;
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_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_frac = CVARF("sv_antilag_frac", "1", CVAR_SERVERINFO);
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", "", CVAR_SERVERINFO);
#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_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_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 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)
@ -2853,7 +2856,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
}
#endif
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.
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_DefaultExtension(tryogg, ".ogg", sizeof(tryogg));
found = FS_FLocateFile(tryogg, FSLFRT_IFFOUND, loc);
found = FS_FLocateFile(tryogg, FSLF_IFFOUND, loc);
if (found)
{
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))
{
//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
*replacementname = pakname;
@ -4367,6 +4370,14 @@ void Cmd_Spiderpig_f(void)
}
void Cmd_Noclip_f (void)
{
#ifdef HLSERVER
if (svs.gametype == GT_HALFLIFE)
{
HLSV_ClientCommand(host_client);
return;
}
#endif
if (!SV_MayCheat())
{
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;
}
if (sv_antilag.ival)
if (sv_antilag.ival || !*sv_antilag.string)
{
/*
extern cvar_t temp1;
@ -7009,7 +7020,7 @@ void SV_ExecuteClientMessage (client_t *cl)
}
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
cl->laggedents_count = 0;
@ -7720,6 +7731,9 @@ void SV_UserInit (void)
Cvar_Register (&sv_spectalk, 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_antilag, 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;
bi_begin();
@ -58,7 +58,7 @@ string_t QDECL GHL_AllocString(char *string)
bi_end();
return news - SVHL_Globals.stringbase;
}
int QDECL GHL_PrecacheModel(char *name)
int QDECL GHL_PrecacheModel(const char *name)
{
int i;
bi_trace();
@ -69,7 +69,7 @@ int QDECL GHL_PrecacheModel(char *name)
return 0;
}
for (i=1 ; i<MAX_MODELS ; i++)
for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
{
if (!sv.strings.model_precache[i])
{
@ -117,7 +117,7 @@ int QDECL GHL_PrecacheSound(char *name)
return 0;
}
for (i=1 ; i<MAX_SOUNDS ; i++)
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
{
if (!*sv.strings.sound_precache[i])
{
@ -204,10 +204,156 @@ void QDECL GHL_VecToAngles(float *inv, float *outa)
bi_trace();
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)
{
//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();
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_ChangePitch(unk){notimpf(__func__);}
@ -243,7 +389,23 @@ hledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *valu
}
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)
{
int i, j;
@ -281,7 +443,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
int best = 0, i;
float bestdist = 99999999; //HL maps are limited in size anyway
float d;
int leafnum;
int clusternum;
vec3_t ofs;
hledict_t *other;
@ -290,7 +452,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
//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)
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++)
{
@ -302,8 +464,8 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
if (svs.clients[i].spectator)
continue; //ignore spectators
leafnum = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/
if (viewerpvs[leafnum>>3] & (1<<(leafnum&7)))
clusternum = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/
if (viewerpvs[clusternum>>3] & (1<<(clusternum&7)))
{
VectorSubtract(ed->v.origin, other->v.origin, ofs);
d = DotProduct(ofs, ofs);
@ -455,11 +617,145 @@ int QDECL GHL_DropToFloor(hledict_t *ed)
VectorCopy(tr.endpos, ed->v.origin);
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();
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)
{
@ -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)
{
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)
{
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)
{
@ -522,7 +820,7 @@ char *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end)
{
trace_t tr;
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;
}
unk QDECL GHL_TraceSphere(unk){notimpf(__func__);}
@ -530,6 +828,8 @@ unk QDECL GHL_GetAimVector(unk){notimpf(__func__);}
void QDECL GHL_ServerCommand(char *cmd)
{
bi_trace();
if (!strcmp(cmd, "reload\n"))
cmd = "restart\n";
Cbuf_AddText(cmd, RESTRICT_PROGS);
}
void QDECL GHL_ServerExecute(void)
@ -541,8 +841,9 @@ unk QDECL GHL_ClientCommand(unk){notimpf(__func__);}
unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);}
void QDECL GHL_LightStyle(int stylenum, char *stylestr)
{
vec3_t rgb = {1,1,1};
bi_trace();
PF_applylightstyle(stylenum, stylestr, 7);
PF_applylightstyle(stylenum, stylestr, rgb);
}
int QDECL GHL_DecalIndex(char *decalname)
{
@ -619,6 +920,9 @@ void QDECL GHL_MessageEnd(unk)
case MSG_MULTICAST+1:
SV_Multicast(svhl_messageorigin, MULTICAST_PHS);
break;
case 9:
//spectators only
break;
default:
Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest);
break;
@ -629,7 +933,7 @@ void QDECL GHL_MessageEnd(unk)
void QDECL GHL_WriteByte(int value)
{
bi_trace();
MSG_WriteByte(&sv.multicast, value);
MSG_WriteByte(&sv.multicast, value & 0xff);
}
void QDECL GHL_WriteChar(int value)
{
@ -730,7 +1034,102 @@ int QDECL GHL_RegUserMsg(char *msgname, int msgsize)
return lastusermessage--;
}
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)
{
@ -772,7 +1171,7 @@ int QDECL GHL_Cmd_Argc(unk)
unk QDECL GHL_GetAttachment(unk){notimpf(__func__);}
void QDECL GHL_CRC32_Init(hlcrc_t *crc)
{
unsigned short crc16 = *crc;
unsigned short crc16;
bi_trace();
QCRC_Init(&crc16);
*crc = crc16;
@ -1324,11 +1723,12 @@ void SV_ReadLibListDotGam(void)
char value[1024];
char *file;
char *s;
size_t fsize;
Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info));
Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info));
file = COM_LoadTempFile("liblist.gam");
file = COM_LoadTempFile("liblist.gam", &fsize);
if (!file)
return;
@ -1368,6 +1768,8 @@ int SVHL_InitGame(void)
{NULL, NULL}
};
memset(&SVHL_Globals, 0, sizeof(SVHL_Globals));
if (sizeof(long) != sizeof(void*))
{
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");
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);
hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs);
@ -1404,7 +1807,7 @@ int SVHL_InitGame(void)
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))
Con_Printf(CON_ERROR "mod is 138\n");
Sys_CloseLibrary(hlgamecode);
@ -1446,7 +1849,12 @@ void SVHL_SpawnEntities(char *entstring)
SVHL_Globals.deathmatch = deathmatch.value;
SVHL_Globals.coop = coop.value;
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;
@ -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
for (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++)
{
sv.strings.model_precache[1+i] = localmodels[i];
sv.models[i+1] = Mod_ForName (Mod_FixName(localmodels[i], sv.modelname), false);
const char *s = va("*%i", i);
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)
@ -1574,6 +1986,19 @@ qboolean HLSV_ClientCommand(client_t *client)
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
if (!hlgamecode)
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();
SVHL_GameFuncs.ClientCommand(ed);
bi_end();
@ -1589,7 +2014,8 @@ qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[1
sv.skipbprintclient = client;
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();
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_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;
}
@ -1621,6 +2047,7 @@ void SVHL_DropClient(client_t *drop)
bi_begin();
SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]);
bi_end();
drop->hledict = NULL;
ed->isfree = true;
}
@ -1636,8 +2063,9 @@ extern cvar_t temp1;
usercmd_t cmd = *ucmd;
cmd.msec = ucmd->msec/2;
cmd.msec_compat = floor(cmd.msec);
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;
SVHL_RunCmdR (ed, &cmd);
return;
@ -1649,7 +2077,22 @@ extern cvar_t temp1;
host_frametime = 0.1;
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.physents[0].model = sv.world.worldmodel;
pmove.physents[0].info = 0;
@ -1742,8 +2185,8 @@ extern cvar_t temp1;
}
}
VectorCopy(ed->v.mins, player_mins);
VectorCopy(ed->v.maxs, player_maxs);
VectorCopy(ed->v.mins, pmove.player_mins);
VectorCopy(ed->v.maxs, pmove.player_maxs);
VectorCopy(ed->v.origin, pmove.origin);
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[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();
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->modelindex = e->v.modelindex;
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->scale = 16;
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.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 *ChangePitch)(unk);
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 *FindClientInPVS)(hledict_t *ed);
unk (QDECL *EntitiesInPVS)(unk);
@ -479,7 +479,7 @@ typedef struct
void *(QDECL *GetModelPtr)(hledict_t *ed);
int (QDECL *RegUserMsg)(char *msgname, int msgsize);
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);
char *(QDECL *NameForFunction)(hlintptr_t);
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
// if (!SVHL_TestEntityPosition (check))
// continue;
if (!SVHL_TestEntityPosition (check))
continue;
}
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)
{
vec3_t vf, vr, vu, angles, v;
// vec3_t vf, vr, vu, angles, v;
hledict_t *e;
// regular thinking
@ -1817,6 +1817,8 @@ void SVHL_RunFrame (void)
//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
if (sv.state == ss_active)
{
for (i = 0; i < sv.allocated_client_slots; i++)
{
if (svs.clients[i].state == cs_spawned)
@ -1824,6 +1826,7 @@ void SVHL_RunFrame (void)
}
if (i == sv.allocated_client_slots)
return;
}
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"
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
@ -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)
{
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;
}
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
@ -396,7 +396,7 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t
//okay, we hit the bbox
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");
model = sv.models[ (int)ent->v.modelindex ];
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)
{
//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;
return true;
}
static void World_ShutdownPhysics(world_t *world)
void World_RBE_Shutdown(world_t *world)
{
unsigned int u;
wedict_t *ed;
@ -2124,11 +2124,11 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename)
#if defined(CSQC_DAT) && !defined(SERVERONLY)
{
extern world_t csqc_world;
World_ShutdownPhysics(&csqc_world);
World_RBE_Shutdown(&csqc_world);
}
#endif
#if !defined(CLIENTONLY)
World_ShutdownPhysics(&sv.world);
World_RBE_Shutdown(&sv.world);
#endif
world_current_physics_engine = NULL;
@ -2136,12 +2136,15 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename)
void World_RBE_Start(world_t *world)
{
if (world_current_physics_engine)
{
if (world->worldmodel)
world_current_physics_engine(world);
}
}
void World_Destroy(world_t *world)
{
World_ShutdownPhysics(world);
World_RBE_Shutdown(world);
Z_Free(world->areanodes);
world->areanodes = NULL;

View file

@ -375,7 +375,6 @@ void SWBE_SelectMode(backendmode_t mode)
void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh)
{
extern cvar_t temp1;
int i;
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);
if (h->id)
history++;
history_max = history; //always break any pending redos
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 =
{
vector col = '0 0 0';
@ -1482,29 +1552,12 @@ brusheditormodes
registercommand("brushedit_matchface");
registercommand("brushedit_resettexcoords");
registercommand("brushedit_settexture");
registercommand("brushedit_subtract");
registercommand("brushedit_binds");
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 =
{
switch(argv(0))
@ -1537,15 +1590,16 @@ brusheditormodes
bt_points = 0;
break;
case "brushedit_resettexcoords":
//IMPLEMENTME
brushtool = BT_NONE;
bt_points = 0;
brushedit_resettextures();
break;
case "brushedit_settexture":
//IMPLEMENTME
brushtool = BT_NONE;
bt_points = 0;
break;
case "brushedit_subtract":
brushedit_subtract();
break;
case "brushedit_delete":
if (selectedbrushmodel && selectedbrush)
brush_history_delete(selectedbrushmodel, selectedbrush);
@ -1553,15 +1607,16 @@ brusheditormodes
break;
case "brushedit_binds":
localcmd("conmenu \"\"\n");
localcmd("menubind 0 8 \"brushedit_create\" \"Creation Tool\"\n");
localcmd("menubind 0 16 \"brushedit_slice\" \"Slice Tool\"\n");
localcmd("menubind 0 24 \"brushedit_delete\" \"Delete\"\n");
localcmd("menubind 0 32 \"brushedit_undo\" \"Undo\"\n");
localcmd("menubind 0 40 \"brushedit_redo\" \"Redo\"\n");
localcmd("menubind 0 48 \"brushedit_nogrid\" \"Disable Grid\"\n");
float foo = 0;
localcmd(sprintf("menubind 0 %g \"brushedit_create\" \"Creation Tool\"\n", foo+=8));
localcmd(sprintf("menubind 0 %g \"brushedit_slice\" \"Slice Tool\"\n", foo+=8));
localcmd(sprintf("menubind 0 %g \"brushedit_delete\" \"Delete\"\n", foo+=8));
localcmd(sprintf("menubind 0 %g \"brushedit_subtract\" \"Subtract\"\n", foo+=8));
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)); foo+=8;
#define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo+=8));
brusheditormodes
#undef brusheditormode
break;
@ -1804,11 +1859,5 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
return TRUE;
}
/* if (key == 's')
{
//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;
return FALSE;
};

View file

@ -133,6 +133,10 @@ inwater <effectname>
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).
overwater
specifies that this
underwater
colorindex <index> [rand]
Specifies a palette index to spawn the particle with.
The index used is between index and index+rand.
@ -279,6 +283,10 @@ emitstart <seconds>
spawnorg <horiz> [vert]
spawnvel <horiz> [vert]
viewspace [frac]
Specifies that this particle type should move relative to the camera.
Not compatible with splitscreen.
perframe
apply inverse frametime to count (causes emits to be per frame)