Added support for culling csqc entities according to pvs (primarily to fix skyrooms, but can also help other situations too). Requires r_ignoreentpvs 0 (compat builds still use 1).

Added some explicit IMGFMT_ constants for qc.
Apply contrast after gamma, instead of before, should give a slightly more colourful result.
Version command now lists supported image formats (separately from extensions).
More BIH fixes.
Add patchDefWS parsing in .map files, for rgba vertex colours.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5444 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2019-04-07 16:41:09 +00:00
parent e581d3ffeb
commit 12d3e48785
32 changed files with 645 additions and 341 deletions

View file

@ -2133,6 +2133,7 @@ void V_AddAxisEntity(entity_t *in)
void V_ClearEntity(entity_t *e)
{
memset(e, 0, sizeof(*e));
e->pvscache.num_leafs = -1;
e->playerindex = -1;
e->topcolour = TOP_DEFAULT;
e->bottomcolour = BOTTOM_DEFAULT;
@ -3342,11 +3343,11 @@ void CL_LinkStaticEntities(void *pvs)
VectorCopy(stat->state.origin, mins);
VectorCopy(stat->state.origin, maxs);
}
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &stat->pvscache, mins, maxs);
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &stat->ent.pvscache, mins, maxs);
}
/*pvs test*/
if (pvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &stat->pvscache, pvs))
if (pvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &stat->ent.pvscache, pvs))
continue;
@ -3918,6 +3919,13 @@ void CL_LinkPacketEntities (void)
le = &cl.lerpents[state->number];
ent = &cl_visedicts[cl_numvisedicts];
ent->pvscache.num_leafs = 0;
#if defined(Q2BSPS) || defined(Q3BSPS) || defined(TERRAIN)
ent->pvscache.areanum = 0;
ent->pvscache.areanum2 = 0;
ent->pvscache.headnode = 0;
#endif
ent->rtype = RT_MODEL;
ent->playerindex = -1;
ent->customskin = 0;

View file

@ -2282,6 +2282,10 @@ void CL_SendCmd (double frametime, qboolean mainloop)
{
CL_SendDownloadReq(&buf);
//only start spamming userinfo blobs once we receive the initial serverinfo.
while (cls.userinfosync.numkeys && cls.netchan.message.cursize < 512 && (cl.haveserverinfo || cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3))
CL_SendUserinfoUpdate();
while (clientcmdlist)
{
next = clientcmdlist->next;
@ -2289,6 +2293,8 @@ void CL_SendCmd (double frametime, qboolean mainloop)
{
if (cls.netchan.message.cursize + 2+strlen(clientcmdlist->command)+100 > cls.netchan.message.maxsize)
break;
if (!strncmp(clientcmdlist->command, "spawn", 5) && cls.userinfosync.numkeys)
break; //HACK: don't send the spawn until all pending userinfos have been flushed.
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
MSG_WriteString (&cls.netchan.message, clientcmdlist->command);
}
@ -2304,10 +2310,6 @@ void CL_SendCmd (double frametime, qboolean mainloop)
Z_Free(clientcmdlist);
clientcmdlist = next;
}
//only start spamming userinfo blobs once we receive the initial serverinfo.
while (cls.userinfosync.numkeys && cls.netchan.message.cursize < 512 && (cl.haveserverinfo || cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3))
CL_SendUserinfoUpdate();
}
// if we're not doing clc_moves and etc, don't continue unless we wrote something previous

View file

@ -3880,10 +3880,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
}
static void CLNQ_SendInitialUserInfo(void *ctx, const char *key, const char *value)
{
char keybuf[2048];
char valbuf[4096];
#warning FIXME: use CL_SendUserinfoUpdate or something
CL_SendClientCommand(true, "setinfo %s %s\n", COM_QuotedString(key, keybuf, sizeof(keybuf), false), COM_QuotedString(value, valbuf, sizeof(valbuf), false));
InfoSync_Add(&cls.userinfosync, ctx, key);
}
void CLNQ_SignonReply (void)
{
@ -3907,7 +3904,7 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
CL_SendClientCommand(true, "name \"%s\"\n", name.string);
CL_SendClientCommand(true, "color %i %i\n", topcolor.ival, bottomcolor.ival);
if (cl.haveserverinfo)
InfoBuf_Enumerate(&cls.userinfo[0], NULL, CLNQ_SendInitialUserInfo);
InfoBuf_Enumerate(&cls.userinfo[0], &cls.userinfo[0], CLNQ_SendInitialUserInfo);
else if (CPNQ_IS_DP)
{ //dp needs a couple of extras to work properly in certain cases. don't send them on other servers because that generally results in error messages.
CL_SendClientCommand(true, "rate %s", rate.string);
@ -4593,7 +4590,7 @@ static void CL_ParseStaticProt (int baselinetype)
cl_static_entities[i].state = es;
ent = &cl_static_entities[i].ent;
V_ClearEntity(ent);
memset(&cl_static_entities[i].pvscache, 0, sizeof(cl_static_entities[i].pvscache));
memset(&cl_static_entities[i].ent.pvscache, 0, sizeof(cl_static_entities[i].ent.pvscache));
ent->keynum = es.number;
@ -4662,7 +4659,7 @@ static void CL_ParseStaticProt (int baselinetype)
VectorCopy(es.origin, mins);
VectorCopy(es.origin, maxs);
}
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].pvscache, mins, maxs);
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].ent.pvscache, mins, maxs);
#ifdef RTLIGHTS
//and now handle any rtlight fields on it

View file

@ -841,10 +841,6 @@ typedef struct
} intermissionmode; // don't change view angle, full screen, etc
float completed_time; // latched ffrom time at intermission start
#define Q2MAX_VISIBLE_WEAPONS 32 //q2 has about 20.
int numq2visibleweapons; //q2 sends out visible-on-model weapons in a wierd gender-nutral way.
char *q2visibleweapons[Q2MAX_VISIBLE_WEAPONS];//model names beginning with a # are considered 'sexed', and are loaded on a per-client basis. yay. :(
//
// information that is static for the entire time connected to a server
//
@ -860,6 +856,10 @@ typedef struct
int particle_ssprecache[MAX_SSPARTICLESPRE]; //these are actually 1-based, so 0 can be used to lazy-init them. I cheat.
#ifdef Q2CLIENT
#define Q2MAX_VISIBLE_WEAPONS 32 //q2 has about 20.
int numq2visibleweapons; //q2 sends out visible-on-model weapons in a wierd gender-nutral way.
char *q2visibleweapons[Q2MAX_VISIBLE_WEAPONS];//model names beginning with a # are considered 'sexed', and are loaded on a per-client basis. yay. :(
char *configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL];
char *image_name[Q2MAX_IMAGES];
char *item_name[Q2MAX_ITEMS];
@ -1043,7 +1043,6 @@ typedef struct
entity_state_t state;
trailstate_t *emit;
int mdlidx; /*negative are csqc indexes*/
pvscache_t pvscache;
} static_entity_t;
// FIXME, allocate dynamically

View file

@ -605,7 +605,6 @@ void M_Menu_Audio_f (void)
};
#ifdef VOICECHAT
static const char *voipcodecoptions[] = {
"Auto",
"Speex (ez-compat)",
// "Raw16 (11025)",
"Opus",
@ -617,8 +616,7 @@ void M_Menu_Audio_f (void)
NULL
};
static const char *voipcodecvalue[] = {
"",
"0", //speex non-standard
"0", //speex non-standard (outdated)
// "1", //pcm16 sucks
"2", //opus
"3", //speex narrow

View file

@ -860,7 +860,7 @@ static float CSQC_PitchScaleForModelIndex(int index)
wedict_t *skel_gettaginfo_args (pubprogfuncs_t *prinst, vec3_t axis[3], vec3_t origin, int tagent, int tagnum);
#endif
static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
static qboolean CopyCSQCEdictToEntity(csqcedict_t *fte_restrict in, entity_t *fte_restrict out)
{
int ival;
model_t *model;
@ -874,6 +874,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
memset(out, 0, sizeof(*out));
out->model = model;
out->pvscache = in->pvsinfo;
rflags = in->xv->renderflags;
if (csqc_isdarkplaces)
@ -882,7 +883,10 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
{
rflags = in->xv->renderflags;
if (rflags & CSQCRF_VIEWMODEL)
{
out->flags |= RF_DEPTHHACK|RF_WEAPONMODEL;
out->pvscache.num_leafs = -1;
}
if (rflags & CSQCRF_EXTERNALMODEL)
out->flags |= RF_EXTERNALMODEL;
if (rflags & CSQCRF_DEPTHHACK)
@ -947,6 +951,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
csqcedict_t *p = (csqcedict_t*)skel_gettaginfo_args(csqcprogs, out->axis, out->origin, in->xv->tag_entity, in->xv->tag_index);
if (p && (int)p->xv->renderflags & CSQCRF_VIEWMODEL)
out->flags |= RF_DEPTHHACK|RF_WEAPONMODEL;
out->pvscache.num_leafs = -1; //make visible globally
#endif
}
@ -1030,9 +1035,9 @@ static void QCBUILTIN PF_cs_makestatic (pubprogfuncs_t *prinst, struct globalvar
cl_static_entities[cl.num_statics].emit = NULL;
cl_static_entities[cl.num_statics].mdlidx = in->v->modelindex;
if (cl.worldmodel && cl.worldmodel->funcs.FindTouchedLeafs)
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[cl.num_statics].pvscache, in->v->absmin, in->v->absmax);
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[cl.num_statics].ent.pvscache, in->v->absmin, in->v->absmax);
else
memset(&cl_static_entities[cl.num_statics].pvscache, 0, sizeof(cl_static_entities[cl.num_statics].pvscache));
memset(&cl_static_entities[cl.num_statics].ent.pvscache, 0, sizeof(cl_static_entities[cl.num_statics].ent.pvscache));
cl.num_statics++;
//rtlights kinda need all this junk
@ -2116,13 +2121,24 @@ uploadfmt_t PR_TranslateTextureFormat(int qcformat)
{
switch(qcformat)
{
case 1: return TF_RGBA32;
case 2: return TF_RGBA16F;
case 3: return TF_RGBA32F;
case 4: return TF_DEPTH16;
case 5: return TF_DEPTH24;
case 6: return TF_DEPTH32;
default:return TF_INVALID;
case 1: return PTI_RGBA8;
case 2: return PTI_RGBA16F;
case 3: return PTI_RGBA32F;
case 4: return PTI_DEPTH16;
case 5: return PTI_DEPTH24;
case 6: return PTI_DEPTH32;
case 7: return PTI_R8;
case 8: return PTI_R16F;
case 9: return PTI_R32F;
case 10: return PTI_A2BGR10;
case 11: return PTI_RGB565;
case 12: return PTI_RGBA4444;
case 13: return PTI_RG8;
default:return PTI_INVALID;
}
}
void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)

View file

@ -1373,7 +1373,7 @@ void R2D_BrightenScreen (void)
RSpeedMark();
if (fabs(v_contrast.value - 1.0) < 0.05 && fabs(v_brightness.value - 0) < 0.05 && fabs(v_gamma.value - 1) < 0.05)
if (fabs(v_contrast.value - 1.0) < 0.05 && fabs(v_contrastboost.value - 1.0) < 0.05 && fabs(v_brightness.value - 0) < 0.05 && fabs(v_gamma.value - 1) < 0.05)
return;
//don't go crazy with brightness. that makes it unusable and is thus unsafe - and worse, lots of people assume its based around 1 (like gamma and contrast are). cap to 0.5
@ -1386,10 +1386,10 @@ void R2D_BrightenScreen (void)
return;
TRACE(("R2D_BrightenScreen: brightening\n"));
if ((v_gamma.value != 1 || v_contrast.value > 3) && shader_gammacb->prog)
if ((v_gamma.value != 1 || v_contrast.value > 3 || v_contrastboost.value != 1) && shader_gammacb->prog)
{
//this should really be done properly, with render-to-texture
R2D_ImageColours (v_gamma.value, v_contrast.value, v_brightness.value, 1);
R2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value);
R2D_Image(0, 0, vid.width, vid.height, 0, 0, 1, 1, shader_gammacb);
}
else

View file

@ -4204,7 +4204,7 @@ TRACE(("dbg: Surf_NewMap: tp\n"));
VectorCopy(maxs, cl_static_entities[i].ent.origin);
}
if (cl.worldmodel->funcs.FindTouchedLeafs)
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].pvscache, mins, maxs);
cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].ent.pvscache, mins, maxs);
cl_static_entities[i].emit = NULL;
}

View file

@ -146,6 +146,8 @@ typedef struct entity_s
struct shader_s *forcedshader;
pvscache_t pvscache; //for culling of csqc ents.
#ifdef PEXT_SCALE
float scale;
#endif
@ -322,7 +324,7 @@ extern struct texture_s *r_notexture_mip;
extern entity_t r_worldentity;
void BE_GenModelBatches(struct batch_s **batches, const struct dlight_s *dl, unsigned int bemode); //if dl, filters based upon the dlight.
void BE_GenModelBatches(struct batch_s **batches, const struct dlight_s *dl, unsigned int bemode, qbyte *worldpvs); //if dl, filters based upon the dlight.
//gl_alias.c
void R_GAliasFlushSkinCache(qboolean final);

View file

@ -105,6 +105,12 @@ extern cvar_t dpcompat_nopremulpics;
cvar_t dpcompat_psa_ungroup = CVAR ("dpcompat_psa_ungroup", "0");
#endif
#ifdef HAVE_LEGACY
cvar_t r_ignoreentpvs = CVARD ("r_ignoreentpvs", "1", "Disables pvs culling of entities that have been submitted to the renderer.");
#else
cvar_t r_ignoreentpvs = CVARD ("r_ignoreentpvs", "0", "Disables pvs culling of entities that have been submitted to the renderer.");
#endif
cvar_t mod_md3flags = CVARD ("mod_md3flags", "1", "The flags field of md3s was never officially defined. If this is set to 1, the flags will be treated identically to mdl files. Otherwise they will be ignored. Naturally, this is required to provide rotating pickups in quake.");
cvar_t r_ambient = CVARF ("r_ambient", "0",
@ -958,6 +964,7 @@ void Renderer_Init(void)
//renderer
Cvar_Register (&r_ignoreentpvs, "Hacky bug workarounds");
Cvar_Register (&r_fullbright, SCREENOPTIONS);
Cvar_Register (&r_drawentities, GRAPHICALNICETIES);
Cvar_Register (&r_drawviewmodel, GRAPHICALNICETIES);

View file

@ -220,11 +220,6 @@ typedef enum uploadfmt
PTI_MAX,
TF_INVALID = PTI_INVALID,
TF_DEPTH16 = PTI_DEPTH16,
TF_DEPTH24 = PTI_DEPTH24,
TF_DEPTH32 = PTI_DEPTH32,
TF_RGBA16F = PTI_RGBA16F,
TF_RGBA32F = PTI_RGBA32F,
TF_RGBA32 = PTI_RGBA8, /*rgba byte order*/
TF_BGRA32 = PTI_BGRA8, /*bgra byte order*/
TF_RGBX32 = PTI_RGBX8, /*rgb byte order, with extra wasted byte after blue*/

View file

@ -288,7 +288,7 @@ enum
#ifdef NOLEGACY
#define VOIP_DEFAULT_CODEC VOIP_OPUS
#else
#define VOIP_DEFAULT_CODEC (cls.protocol==CP_QUAKEWORLD?VOIP_SPEEX_OLD:VOIP_OPUS) //opus is preferred, but ezquake is still common and only supports my first attempt at voice compression so favour that for quakeworld.
#define VOIP_DEFAULT_CODEC ((cls.protocol==CP_QUAKEWORLD && !(cls.fteprotocolextensions2&PEXT2_REPLACEMENTDELTAS))?VOIP_SPEEX_OLD:VOIP_OPUS) //opus is preferred, but ezquake is still common and only supports my first attempt at voice compression so favour that for mvdsv servers.
#endif
static struct
{

View file

@ -333,8 +333,10 @@ cshift_t cshift_lava = { {255,80,0}, 150 };
cshift_t cshift_server = { {130,80,50}, 0 };
cvar_t v_gamma = CVARFCD("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, V_Gamma_Callback, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate.");
cvar_t v_contrast = CVARFCD("contrast", "1.0", CVAR_ARCHIVE, V_Gamma_Callback, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.");
cvar_t v_brightness = CVARFCD("brightness", "0.0", CVAR_ARCHIVE, V_Gamma_Callback, "Brightness is how much 'white' to add to each and every pixel on the screen.");
cvar_t v_gammainverted = CVARFCD("v_gammainverted", "0", CVAR_ARCHIVE, V_Gamma_Callback, "Boolean that controls whether the gamma should be inverted (like quake) or not.");
cvar_t v_contrast = CVARAFCD("contrast", "1.0", "v_contrast", CVAR_ARCHIVE, V_Gamma_Callback, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.");
cvar_t v_contrastboost = CVARFCD("v_contrastboost", "1.0", CVAR_ARCHIVE, V_Gamma_Callback, "Amplifies contrast in dark areas");
cvar_t v_brightness = CVARAFCD("brightness", "0.0", "v_contrast", CVAR_ARCHIVE, V_Gamma_Callback, "Brightness is how much 'white' to add to each and every pixel on the screen.");
qbyte gammatable[256]; // palette is sent through this
@ -363,14 +365,15 @@ void BuildGammaTable (float g)
gammatable[i] = inf;
}
}*/
void BuildGammaTable (float g, float c, float b)
void BuildGammaTable (float gamma, float cscale, float cboost, float brightness)
{
int i, inf;
float t;
// g = bound (0.1, g, 3);
// c = bound (1, c, 3);
// gamma = bound (0.1, gamma, 3);
// cscale = bound (1, cscale, 3);
if (g == 1 && c == 1)
if (gamma == 1 && cscale == 1 && cboost == 1 && brightness == 0)
{
for (i = 0; i < 256; i++)
gammatable[i] = i;
@ -380,12 +383,10 @@ void BuildGammaTable (float g, float c, float b)
for (i = 0; i < 256; i++)
{
//the 0.5s are for rounding.
inf = 255 * (pow ((i + 0.5) / 255.5 * c, g) + b) + 0.5;
if (inf < 0)
inf = 0;
else if (inf > 255)
inf = 255;
gammatable[i] = inf;
t = (i + 0.5) / 255.5; //scale the lighting
t = cboost * t/((cboost-1)*t + 1);
inf = 255 * (pow (t, gamma)*cscale + brightness) + 0.5;
gammatable[i] = bound(0, inf, 255);
}
}
@ -396,7 +397,7 @@ V_CheckGamma
*/
static void QDECL V_Gamma_Callback(struct cvar_s *var, char *oldvalue)
{
BuildGammaTable (v_gamma.value, v_contrast.value, v_brightness.value);
BuildGammaTable (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_contrastboost.value, v_brightness.value);
V_UpdatePalette (true);
}
@ -2582,9 +2583,10 @@ void V_Init (void)
Cvar_Register (&ffov, VIEWVARS);
Cvar_Register (&r_projection, VIEWVARS);
BuildGammaTable (1.0, 1.0, 0.0); // no gamma yet
BuildGammaTable (1.0, 1.0, 1.0, 0.0); // no gamma yet
Cvar_Register (&v_gamma, VIEWVARS);
Cvar_Register (&v_contrast, VIEWVARS);
Cvar_Register (&v_contrastboost, VIEWVARS);
Cvar_Register (&v_brightness, VIEWVARS);
Cvar_Register (&chase_active, VIEWVARS);

View file

@ -20,7 +20,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// view.h
extern cvar_t v_gamma;
extern cvar_t v_gammainverted;
extern cvar_t v_contrast;
extern cvar_t v_contrastboost;
extern cvar_t v_brightness;
extern float sw_blend[4];
extern float hw_blend[4];

View file

@ -142,6 +142,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#endif
#endif
#ifndef NOLEGACY
#define HAVE_LEGACY
#endif
#ifndef HAVE_SERVER
#undef MVD_RECORDING
@ -542,7 +545,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef Q3CLIENT //reconsider this (later)
#undef Q3SERVER //reconsider this (later)
#endif
#ifdef DEBUG
#if defined(DEBUG) || defined(_DEBUG)
#undef NOQCDESCRIPTIONS //don't disable writing fteextensions.qc in debug builds, otherwise how would you ever build one? :o
#endif
@ -691,6 +694,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define NORETURN __attribute__((noreturn))
#endif
//unreachable marks the path leading to it as unreachable too.
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define FTE_UNREACHABLE __builtin_unreachable()
#endif
//I'm making my own restrict, because msvc's headers can't cope if I #define restrict to __restrict, and quite possibly other platforms too
#if __STDC_VERSION__ >= 199901L
#define fte_restrict restrict
@ -739,6 +747,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef FTE_DEPRECATED
#define FTE_DEPRECATED
#endif
#ifndef FTE_UNREACHABLE
#define FTE_UNREACHABLE
#endif
#ifndef LIKEPRINTF
#define LIKEPRINTF(x)
#endif
@ -754,6 +765,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define ZEXPORTVA VARGS
#endif
#ifdef _DEBUG
#undef FTE_UNREACHABLE
#define FTE_UNREACHABLE Sys_Error("Unreachable reached: %s %i\n", __FILE__, __LINE__)
#endif
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
#define CACHE_SIZE 32 // used to align key data structures

View file

@ -4970,70 +4970,97 @@ static void COM_Version_f (void)
Con_Printf("zlib: %s\n", ZLIB_VERSION);
#endif
#ifndef SERVERONLY
//but print client ones only if we're not dedicated
#ifndef AVAIL_PNGLIB
Con_Printf("libpng disabled\n");
#else
#ifdef DYNAMIC_LIBPNG
Con_Printf("libPNG(dynamic) %s -%s", PNG_LIBPNG_VER_STRING, PNG_HEADER_VERSION_STRING);
#else
Con_Printf("libPNG %s -%s", PNG_LIBPNG_VER_STRING, PNG_HEADER_VERSION_STRING);
#ifdef HAVE_CLIENT
Con_Printf("image formats:");
#ifdef IMAGEFMT_DDS
Con_Printf(" dds");
#endif
#endif
#ifndef AVAIL_JPEGLIB
Con_Printf("libjpeg disabled\n");
#else
#ifdef DYNAMIC_LIBJPEG
Con_Printf("libjpeg(dynamic): %i (%d series)\n", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#else
Con_Printf("libjpeg: %i (%d series)\n", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#ifdef IMAGEFMT_KTX
Con_Printf(" ktx");
#endif
#endif
Con_Printf(" tga");
#if defined(AVAIL_PNGLIB)
Con_Printf(" png");
#ifdef DYNAMIC_LIBPNG
Con_Printf("^h(dynamic, %s)", PNG_LIBPNG_VER_STRING);
#else
Con_Printf("^h(%s)", PNG_LIBPNG_VER_STRING);
#endif
#else
Con_DPrintf(" ^h(disabled: png)");
#endif
#ifdef IMAGEFMT_BMP
Con_Printf(" bmp+ico");
#endif
#if defined(AVAIL_JPEGLIB)
Con_Printf(" jpeg");
#ifdef DYNAMIC_LIBJPEG
Con_Printf("^h(dynamic, %i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#else
Con_Printf("^h(%i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#endif
#else
Con_DPrintf(" ^h(disabled: jpeg)");
#endif
#ifdef IMAGEFMT_PBM
Con_Printf(" pfm+pbm+pgm+ppm"/*"+pam"*/);
#endif
#ifdef IMAGEFMT_PSD
Con_Printf(" psd");
#endif
#ifdef IMAGEFMT_HDR
Con_Printf(" hdr");
#endif
#ifdef IMAGEFMT_PKM
Con_Printf(" pkm");
#endif
#ifdef IMAGEFMT_PCX
Con_Printf(" pcx");
#endif
Con_Printf("\n");
Con_Printf("VoiceChat:");
#if !defined(VOICECHAT)
Con_Printf(" disabled");
#else
#ifdef SPEEX_STATIC
Con_Printf(" speex");
Con_DPrintf("(static)");
#if !defined(VOICECHAT)
Con_Printf(" disabled");
#else
Con_Printf(" speex(dynamic)");
#ifdef SPEEX_STATIC
Con_Printf(" speex");
Con_DPrintf("^h(static)");
#else
Con_Printf(" speex^h(dynamic)");
#endif
#ifdef OPUS_STATIC
Con_Printf(" opus");
Con_DPrintf("^h(static)");
#else
Con_Printf(" opus^h(dynamic)");
#endif
#endif
#ifdef OPUS_STATIC
Con_Printf(" opus");
Con_DPrintf("(static)");
#else
Con_Printf(" opus(dynamic)");
#endif
#endif
Con_Printf("\n");
Con_Printf("Audio Decoders:");
#ifndef AVAIL_OGGVORBIS
Con_DPrintf(" ^h(disabled: Ogg Vorbis)^7");
#elif defined(LIBVORBISFILE_STATIC)
Con_Printf(" Ogg Vorbis");
#else
Con_Printf(" Ogg Vorbis(dynamic)");
#endif
#if defined(AVAIL_MP3_ACM)
Con_Printf(" mp3(system)");
#endif
#ifndef AVAIL_OGGVORBIS
Con_DPrintf(" ^h(disabled: Ogg Vorbis)^7");
#elif defined(LIBVORBISFILE_STATIC)
Con_Printf(" Ogg Vorbis");
#else
Con_Printf(" Ogg Vorbis^h(dynamic)");
#endif
#if defined(AVAIL_MP3_ACM)
Con_Printf(" mp3(system)");
#endif
Con_Printf("\n");
#endif
#ifdef SQL
Con_Printf("Databases:");
#ifdef USE_MYSQL
Con_Printf(" mySQL(dynamic)");
Con_Printf(" mySQL^h(dynamic)");
#else
Con_DPrintf(" ^h(disabled: mySQL)^7");
#endif
#ifdef USE_SQLITE
Con_Printf(" sqlite(dynamic)");
Con_Printf(" sqlite^h(dynamic)");
#else
Con_DPrintf(" ^h(disabled: sqlite)^7");
#endif
@ -5046,19 +5073,19 @@ static void COM_Version_f (void)
#else
Con_DPrintf(" ^h(disabled: mapcluster)^7");
#endif
#ifndef SERVERONLY
#ifdef HAVE_SERVER
#ifdef AVAIL_FREETYPE
#ifdef FREETYPE_STATIC
Con_Printf(" freetype2");
Con_DPrintf("(static)");
Con_DPrintf("^h(static)");
#else
Con_Printf(" freetype2(dynamic)");
Con_Printf(" freetype2^h(dynamic)");
#endif
#else
Con_DPrintf(" ^h(disabled: freetype2)^7");
#endif
#ifdef AVAIL_OPENAL
Con_Printf(" openal(dynamic)");
Con_Printf(" openal^h(dynamic)");
#else
Con_DPrintf(" ^h(disabled: openal)^7");
#endif
@ -5125,7 +5152,7 @@ static void COM_Version_f (void)
Con_Printf(" ssq1qvm");
#endif
#if defined(VM_LUA)
Con_Printf(" ssq1lua(dynamic)");
Con_Printf(" ssq1lua^h(dynamic)");
#endif
#if defined(MENU_DAT)
Con_Printf(" menuqc");
@ -5136,7 +5163,7 @@ static void COM_Version_f (void)
#if defined(CSQC_DAT)
Con_Printf(" csqc");
#endif
#ifndef CLIENTONLY
#ifdef HAVE_SERVER
Con_Printf(" ssqc");
#endif
Con_Printf("\n");

View file

@ -3077,10 +3077,11 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define ZFIXHACK "set r_polygonoffset_submodel_offset 25\nset r_polygonoffset_submodel_factor 0.05\n"
#endif
/*quake requires a few settings for compatibility*/
/*ezquake cheats and compat*/
#define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n"
/*quake requires a few settings for compatibility*/
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8"
#define QCFG "set con_stayhidden 0\nset com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT
/*NetQuake reconfiguration, to make certain people feel more at home...*/
#define NQCFG "//-nohome\ncfg_save_auto 1\n" QCFG "sv_nqplayerphysics 1\ncl_loopbackprotocol auto\ncl_sbar 1\nplug_sbar 0\nsv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\n"
//nehahra has to be weird with its extra cvars, and buggy fullbrights.
@ -3095,9 +3096,9 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
/*yay q2!*/
#define Q2CFG "set com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\n"
#define Q2CFG "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\n"
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
#define Q3CFG "set com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\n"
#define Q3CFG "set v_gammainverted 0\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\n"
//#define RMQCFG "sv_bigcoords 1\n"
typedef struct {

View file

@ -6012,21 +6012,29 @@ struct bihnode_s
BIH_X,
BIH_Y,
BIH_Z,
BIH_GROUP,
BIH_BRUSH,
BIH_PATCHBRUSH,
BIH_TRISOUP,
} type;
union
{
struct{
vec3_t temp_mins;
vec3_t temp_maxs;
int firstchild;
int numchildren;
} group;
struct{
int firstchild;
float cmin[2];
float cmax[2];
};
union bihdata_s{
q2cbrush_t *brush;
q2cbrush_t *patchbrush;
} node;
struct bihdata_s{
unsigned int contents;
union {
q2cbrush_t *brush;
q2cbrush_t *patchbrush;
q3cmesh_t *cmesh;
};
} data;
};
};
@ -6041,177 +6049,213 @@ struct bihtrace_s
vec3_t expand;
qboolean negativedir[3];
vec_t *startpos; //bounds.[min|max]
vec3_t startpos; //bounds.[min|max]
vec3_t totalmove;
vec_t *endpos; //bounds.[min|max]
vec3_t endpos; //bounds.[min|max]
trace_t *trace;
};
static void CM_RecursiveBIHTrace (struct bihtrace_s *fte_restrict tr, const struct bihnode_s *fte_restrict node, struct bihbox_s *fte_restrict movesubbounds, struct bihbox_s *fte_restrict nodebox)
static void CM_RecursiveBIHTrace (struct bihtrace_s *fte_restrict tr, const struct bihnode_s *fte_restrict node, const struct bihbox_s *fte_restrict movesubbounds, const struct bihbox_s *fte_restrict nodebox)
{
//if the tree were 1d, we wouldn't need to be so careful with the bounds, but if the trace is long then we want to avoid hitting all surfaces within that entire-map-encompassing move aabb
if (node->type > BIH_Z)
switch(node->type)
{ //leaf
if (node->type == BIH_BRUSH)
case BIH_BRUSH:
{
q2cbrush_t *b = node->data.brush;
if (b->contents & trace_contents)
if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))
if (node->data.contents & trace_contents)
if (BoundsIntersect(b->absmins, b->absmaxs, movesubbounds->min, movesubbounds->max))
CM_ClipBoxToBrush (tr->size.min, tr->size.max, tr->startpos, tr->endpos, tr->trace, b);
}
else //if (node->type == BIH_PATCHBRUSH)
return;
case BIH_PATCHBRUSH:
{
q2cbrush_t *b = node->data.patchbrush;
if (b->contents & trace_contents)
if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))
if (node->data.contents & trace_contents)
if (BoundsIntersect(b->absmins, b->absmaxs, movesubbounds->min, movesubbounds->max))
CM_ClipBoxToPatch (tr->size.min, tr->size.max, tr->startpos, tr->endpos, tr->trace, b);
}
}
else
{ //node (x y or z)
struct bihbox_s bounds;
struct bihbox_s newbounds;
float distnear, distfar, nearfrac, farfrac, min, max;
unsigned int axis = node->type, child;
return;
case BIH_TRISOUP:
{
q3cmesh_t *cmesh = node->data.cmesh;
if (node->data.contents & trace_contents)
if (BoundsIntersect(cmesh->absmins, cmesh->absmaxs, movesubbounds->min, movesubbounds->max))
Mod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);
}
return;
case BIH_GROUP:
{
int i;
for (i = 0; i < node->group.numchildren; i++)
CM_RecursiveBIHTrace(tr, node+node->group.firstchild+i, movesubbounds, nodebox);
}
return;
case BIH_X:
case BIH_Y:
case BIH_Z:
{
struct bihbox_s bounds;
struct bihbox_s newbounds;
float distnear, distfar, nearfrac, farfrac, min, max;
unsigned int axis = node->type, child;
vec3_t points[2];
if (!tr->totalmove[axis])
{ //doesn't move with respect to this axis. don't allow infinities.
for (child = 0; child < 2; child++)
{ //only recurse if we are actually within the child
min = node->cmin[child] - tr->expand[axis];
max = node->cmax[child] + tr->expand[axis];
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
if (!tr->totalmove[axis])
{ //doesn't move with respect to this axis. don't allow infinities.
for (child = 0; child < 2; child++)
{ //only recurse if we are actually within the child
min = node->node.cmin[child] - tr->expand[axis];
max = node->node.cmax[child] + tr->expand[axis];
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
{
bounds = *nodebox;
bounds.min[axis] = min;
bounds.max[axis] = max;
CM_RecursiveBIHTrace(tr, node+node->node.firstchild+child, movesubbounds, &bounds);
}
}
}
else if (tr->negativedir[axis])
{ //trace goes from right to left so favour the right.
for (child = 2; child-- > 0;)
{
bounds = *nodebox;
bounds.min[axis] = min;
bounds.max[axis] = max;
CM_RecursiveBIHTrace(tr, node+node->firstchild+child, movesubbounds, &bounds);
}
}
}
else if (tr->negativedir[axis])
{ //trace goes from right to left so favour the right.
for (child = 2; child-- > 0;)
{
bounds = *nodebox;
bounds.min[axis] = node->cmin[child] - tr->expand[axis];
bounds.max[axis] = node->cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size
bounds.min[axis] = node->node.cmin[child] - tr->expand[axis];
bounds.max[axis] = node->node.cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size
// CM_RecursiveBIHTrace(tr, node+node->firstchild+child, nodebox, &bounds);
// continue;
if (!BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))
continue;
// if (movesubbounds->max[axis] < bounds.min[axis])
// continue; //(clipped) move bounds is outside this child
// if (bounds.max[axis] < movesubbounds->min[axis])
// continue; //(clipped) move bounds is outside this child
if (bounds.min[axis] > movesubbounds->max[axis])
continue; //(clipped) move bounds is outside this child
if (bounds.max[axis] < movesubbounds->min[axis])
continue; //(clipped) move bounds is outside this child
distnear = bounds.max[axis] - tr->startpos[axis];
nearfrac = distnear/tr->totalmove[axis];
// if (/*nearfrac <= trace_truefraction &&*/ tr->startpos[axis] >= bounds.min[axis] && tr->trace->endpos[axis] <= bounds.max[axis])
{
//we need to be careful when tracing, so that we can ignore children that do not overlap the trace
if (movesubbounds->min[axis] < bounds.min[axis])
distnear = bounds.max[axis] - tr->startpos[axis];
nearfrac = distnear/tr->totalmove[axis];
if (nearfrac <= trace_truefraction)
{
VectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]); //clip the new movebounds (this is more to clip the other axis too)
distfar = bounds.min[axis] - tr->startpos[axis];
farfrac = distfar/tr->totalmove[axis];
VectorMA(tr->startpos, farfrac, tr->totalmove, newbounds.min); //clip the new movebounds (this is more to clip the other axis too)
VectorMA(tr->startpos, farfrac, tr->totalmove, points[1]); //clip the new movebounds (this is more to clip the other axis too)
newbounds.min[0] = max(points[tr->negativedir[0]][0]-tr->expand[axis], bounds.min[0]); newbounds.max[0] = min(points[!tr->negativedir[0]][0]+tr->expand[axis], bounds.max[0]);
newbounds.min[1] = max(points[tr->negativedir[1]][1]-tr->expand[axis], bounds.min[1]); newbounds.max[1] = min(points[!tr->negativedir[1]][1]+tr->expand[axis], bounds.max[1]);
newbounds.min[2] = max(points[tr->negativedir[2]][2]-tr->expand[axis], bounds.min[2]); newbounds.max[2] = min(points[!tr->negativedir[2]][2]+tr->expand[axis], bounds.max[2]);
CM_RecursiveBIHTrace(tr, node+node->node.firstchild+child, &newbounds, &bounds);
}
else
VectorCopy(movesubbounds->min, newbounds.min);
if (bounds.max[axis] < movesubbounds->max[axis])
VectorMA(tr->startpos, nearfrac, tr->totalmove, newbounds.max);
else
VectorCopy(movesubbounds->max, newbounds.max);
CM_RecursiveBIHTrace(tr, node+node->firstchild+child, &newbounds, &bounds);
}
}
}
else
{ //trace goes from left to right
for (child = 0; child < 2; child++)
{
bounds = *nodebox;
bounds.min[axis] = node->cmin[child] - tr->expand[axis];
bounds.max[axis] = node->cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size
// CM_RecursiveBIHTrace(tr, node+node->firstchild+child, nodebox, &bounds);
// continue;
if (bounds.min[axis] > movesubbounds->max[axis])
continue; //(clipped) move bounds is outside this child
if (bounds.max[axis] < movesubbounds->min[axis])
continue; //(clipped) move bounds is outside this child
distnear = bounds.min[axis] - tr->startpos[axis];
nearfrac = distnear/tr->totalmove[axis];
// if (/*nearfrac <= trace_truefraction &&*/ tr->startpos[axis] <= bounds.max[axis] && tr->trace->endpos[axis] >= bounds.min[axis])
else
{ //trace goes from left to right
for (child = 0; child < 2; child++)
{
//we need to be careful when tracing, so that we can ignore children that do not overlap the trace
if (movesubbounds->min[axis] < bounds.min[axis])
VectorMA(tr->startpos, nearfrac, tr->totalmove, newbounds.min); //clip the new movebounds (this is more to clip the other axis too)
else
VectorCopy(movesubbounds->min, newbounds.min);
if (bounds.max[axis] < movesubbounds->max[axis])
bounds = *nodebox;
bounds.min[axis] = node->node.cmin[child] - tr->expand[axis];
bounds.max[axis] = node->node.cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size
if (!BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))
continue;
// if (movesubbounds->max[axis] < bounds.min[axis])
// continue; //(clipped) move bounds is outside this child
// if (bounds.max[axis] < movesubbounds->min[axis])
// continue; //(clipped) move bounds is outside this child
distnear = bounds.min[axis] - tr->startpos[axis];
nearfrac = distnear/tr->totalmove[axis];
if (nearfrac <= trace_truefraction)
{
VectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]); //clip the new movebounds (this is more to clip the other axis too)
distfar = bounds.max[axis] - tr->startpos[axis];
farfrac = distfar/tr->totalmove[axis];
VectorMA(tr->startpos, farfrac, tr->totalmove, newbounds.max);
VectorMA(tr->startpos, farfrac, tr->totalmove, points[1]); //clip the new movebounds (this is more to clip the other axis too)
newbounds.min[0] = max(points[tr->negativedir[0]][0]-tr->expand[axis], bounds.min[0]); newbounds.max[0] = min(points[!tr->negativedir[0]][0]+tr->expand[axis], bounds.max[0]);
newbounds.min[1] = max(points[tr->negativedir[1]][1]-tr->expand[axis], bounds.min[1]); newbounds.max[1] = min(points[!tr->negativedir[1]][1]+tr->expand[axis], bounds.max[1]);
newbounds.min[2] = max(points[tr->negativedir[2]][2]-tr->expand[axis], bounds.min[2]); newbounds.max[2] = min(points[!tr->negativedir[2]][2]+tr->expand[axis], bounds.max[2]);
CM_RecursiveBIHTrace(tr, node+node->node.firstchild+child, &newbounds, &bounds);
}
else
VectorCopy(movesubbounds->max, newbounds.max);
CM_RecursiveBIHTrace(tr, node+node->firstchild+child, &newbounds, &bounds);
}
}
}
return;
}
FTE_UNREACHABLE;
}
static void CM_RecursiveBIHTest (struct bihtrace_s *fte_restrict tr, const struct bihnode_s *fte_restrict node)
{ //with BIH, its possible for a large child node to have a box larger than its sibling.
if (node->type > BIH_Z)
{ //leaf
if (node->type == BIH_BRUSH)
switch(node->type)
{
case BIH_BRUSH:
{
q2cbrush_t *b = node->data.brush;
if (b->contents & trace_contents)
if (node->data.contents & trace_contents)
// if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))
CM_TestBoxInBrush (tr->size.min, tr->size.max, tr->startpos, tr->trace, b);
}
else //if (node->type == BIH_PATCHBRUSH)
return;
case BIH_PATCHBRUSH:
{
q2cbrush_t *b = node->data.patchbrush;
if (b->contents & trace_contents)
if (node->data.contents & trace_contents)
// if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))
CM_TestBoxInPatch (tr->size.min, tr->size.max, tr->startpos, tr->trace, b);
}
}
else
{ //node (x y or z)
float min; float max;
int axis = node->type;
min = node->cmin[0] - tr->expand[axis];
max = node->cmax[0] + tr->expand[axis]; //expand the bounds according to the player's size
//the point can potentially be within both children, or neither.
//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
return;
case BIH_TRISOUP:
{
CM_RecursiveBIHTest(tr, node+node->firstchild+0);
if (trace_trace.allsolid)
return;
q3cmesh_t *cmesh = node->data.cmesh;
if (node->data.contents & trace_contents)
// if (BoundsIntersect(cmesh->absmins, cmesh->absmaxs, tr->bounds.min, tr->bounds.max))
Mod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);
}
return;
case BIH_GROUP:
{
int i;
for (i = 0; i < node->group.numchildren; i++)
{
CM_RecursiveBIHTest(tr, node+node->group.firstchild+i);
if (trace_trace.allsolid)
break;
}
}
return;
case BIH_X:
case BIH_Y:
case BIH_Z:
{ //node (x y or z)
float min; float max;
int axis = node->type;
min = node->node.cmin[0] - tr->expand[axis];
max = node->node.cmax[0] + tr->expand[axis]; //expand the bounds according to the player's size
min = node->cmin[1] - tr->expand[axis];
max = node->cmax[1] + tr->expand[axis];
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
return CM_RecursiveBIHTest(tr, node+node->firstchild+1);
//the point can potentially be within both children, or neither.
//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
{
CM_RecursiveBIHTest(tr, node+node->node.firstchild+0);
if (trace_trace.allsolid)
return;
}
min = node->node.cmin[1] - tr->expand[axis];
max = node->node.cmax[1] + tr->expand[axis];
if (min <= tr->startpos[axis] && tr->startpos[axis] <= max)
return CM_RecursiveBIHTest(tr, node+node->node.firstchild+1);
}
return;
}
FTE_UNREACHABLE;
}
static unsigned int CM_PointContentsBIH (const struct bihnode_s *fte_restrict node, const vec3_t p)
{
if (node->type > BIH_Z)
switch(node->type)
{ //leaf
if (node->type == BIH_BRUSH)
case BIH_BRUSH:
{
q2cbrush_t *b = node->data.brush;
q2cbrushside_t *brushside = b->brushside;
@ -6226,26 +6270,42 @@ static unsigned int CM_PointContentsBIH (const struct bihnode_s *fte_restrict no
}
return b->contents; //inside all planes
}
else //if (node->type == BIH_PATCHBRUSH)
case BIH_PATCHBRUSH:
{ //patches have no contents...
return 0;
}
}
else
{ //node (x y or z)
unsigned int contents;
case BIH_TRISOUP:
{
//trisoup has no contents...
return 0;
}
case BIH_GROUP:
{
int i;
unsigned int contents = 0;
for (i = 0; i < node->group.numchildren; i++)
contents |= CM_PointContentsBIH(node+node->group.firstchild+i, p);
return contents;
}
case BIH_X:
case BIH_Y:
case BIH_Z:
{ //node (x y or z)
unsigned int contents;
//the point can potentially be within both children, or neither.
//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.
if (node->cmin[0] <= p[node->type] && p[node->type] <= node->cmax[0])
contents = CM_PointContentsBIH(node+node->firstchild+0, p);
else
contents = 0;
//the point can potentially be within both children, or neither.
//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.
if (node->node.cmin[0] <= p[node->type] && p[node->type] <= node->node.cmax[0])
contents = CM_PointContentsBIH(node+node->node.firstchild+0, p);
else
contents = 0;
if (node->cmin[1] <= p[node->type] && p[node->type] <= node->cmax[1])
contents |= CM_PointContentsBIH(node+node->firstchild+1, p);
return contents;
if (node->node.cmin[1] <= p[node->type] && p[node->type] <= node->node.cmax[1])
contents |= CM_PointContentsBIH(node+node->node.firstchild+1, p);
return contents;
}
}
FTE_UNREACHABLE;
}
struct bihleaf_s
@ -6253,7 +6313,7 @@ struct bihleaf_s
int type;
vec3_t mins;
vec3_t maxs;
union bihdata_s data;
struct bihdata_s data;
};
static int QDECL CM_SortBIH_X (const void *va, const void *vb)
@ -6288,10 +6348,38 @@ static struct bihbox_s CM_BuildBIHNode (struct bihnode_s *node, struct bihnode_s
struct bihbox_s bounds;
if (numleafs == 1) //the leaf just gives the brush pointer.
{
size_t i;
VectorCopy(leafs[0].mins, bounds.min);
VectorCopy(leafs[0].maxs, bounds.max);
node->type = leafs[0].type;
node->data = leafs[0].data;
//expand by 1qu, to avoid precision issues.
for (i = 0; i < 3; i++)
{
bounds.min[i] -= 1;
bounds.max[i] += 1;
}
}
else if (numleafs < 8) //the leaf just gives the brush pointer.
{
struct bihnode_s *cnodes;
struct bihbox_s cb;
size_t i;
node->type = BIH_GROUP;
cnodes = *freenodes;
*freenodes += numleafs;
node->group.firstchild = cnodes - node;
node->group.numchildren = numleafs;
bounds = CM_BuildBIHNode(cnodes+0, freenodes, leafs+0, 1);
for (i = 1; i < numleafs; i++)
{
cb = CM_BuildBIHNode(cnodes+i, freenodes, leafs+i, 1);
AddPointToBounds(cb.min, bounds.min, bounds.max);
AddPointToBounds(cb.max, bounds.min, bounds.max);
}
}
else
{
@ -6299,8 +6387,8 @@ static struct bihbox_s CM_BuildBIHNode (struct bihnode_s *node, struct bihnode_s
size_t numleft = numleafs / 2; //this ends up splitting at the median point.
size_t numright = numleafs - numleft;
struct bihbox_s left, right;
vec3_t size;
struct bihnode_s *cnodes;
static int (QDECL *sorts[3]) (const void *va, const void *vb) = {CM_SortBIH_X, CM_SortBIH_Y, CM_SortBIH_Z};
VectorCopy(leafs[0].mins, bounds.min);
VectorCopy(leafs[0].maxs, bounds.max);
for (i = 1; i < numleafs; i++)
@ -6313,36 +6401,61 @@ static struct bihbox_s CM_BuildBIHNode (struct bihnode_s *node, struct bihnode_s
bounds.max[j] = leafs[i].maxs[j];
}
}
VectorSubtract(bounds.max, bounds.min, size);
if (size[0] > size[1] && size[0] > size[2])
{
qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_X);
node->type = BIH_X;
#if 1
{ //balanced by counts
vec3_t mid;
int onleft[3], onright[3], weight[3];
VectorAvg(bounds.max, bounds.min, mid);
VectorClear(onleft);
VectorClear(onright);
for (i = 0; i < numleafs; i++)
{
for (j = 0; j < 3; j++)
{ //ignore leafs that split the node.
if (leafs[i].maxs[j] < mid[j])
onleft[j]++;
if (mid[j] > leafs[i].mins[j])
onright[j]++;
}
}
for (j = 0; j < 3; j++)
weight[j] = onleft[j]+onright[j] - abs(onleft[j]-onright[j]);
//pick the most balanced.
if (weight[0] > weight[1] && weight[0] > weight[2])
node->type = BIH_X;
else if (weight[1] > weight[2])
node->type = BIH_Y;
else
node->type = BIH_Z;
}
else if (size[1] > size[2])
{
qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_Y);
node->type = BIH_Y;
#else
{ //balanced by volume
vec3_t size;
VectorSubtract(bounds.max, bounds.min, size);
if (size[0] > size[1] && size[0] > size[2])
node->type = BIH_X;
else if (size[1] > size[2])
node->type = BIH_Y;
else
node->type = BIH_Z;*/
}
else
{
qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_Z);
node->type = BIH_Z;
}
VectorCopy(bounds.min, node->temp_mins);
VectorCopy(bounds.max, node->temp_maxs);
#endif
qsort(leafs, numleafs, sizeof(*leafs), sorts[node->type]);
cnodes = *freenodes;
*freenodes += 2;
node->firstchild = cnodes - node;
node->node.firstchild = cnodes - node;
left = CM_BuildBIHNode (cnodes+0, freenodes, leafs, numleft);
right = CM_BuildBIHNode (cnodes+1, freenodes, &leafs[numleft], numright);
node->cmin[0] = left.min[node->type];
node->cmax[0] = left.max[node->type];
node->cmin[1] = right.min[node->type];
node->cmax[1] = right.max[node->type];
node->node.cmin[0] = left.min[node->type];
node->node.cmax[0] = left.max[node->type];
node->node.cmin[1] = right.min[node->type];
node->node.cmax[1] = right.max[node->type];
bounds = left;
AddPointToBounds(right.min, bounds.min, bounds.max);
AddPointToBounds(right.max, bounds.min, bounds.max);
}
return bounds;
}
@ -6361,6 +6474,7 @@ static struct bihnode_s *CM_BuildBIH (model_t *mod, cminfo_t *prv)
{
q2cbrush_t *b = &prv->brushes[i];
leaf->type = BIH_BRUSH;
leaf->data.contents = b->contents;
leaf->data.brush = b;
VectorCopy(b->absmins, leaf->mins);
VectorCopy(b->absmaxs, leaf->maxs);
@ -6371,6 +6485,7 @@ static struct bihnode_s *CM_BuildBIH (model_t *mod, cminfo_t *prv)
for (j = 0; j < p->numfacets; j++, leaf++)
{
leaf->type = BIH_PATCHBRUSH;
leaf->data.contents = p->facets[j].contents;
leaf->data.patchbrush = &p->facets[j];
VectorCopy(p->facets[j].absmins, leaf->mins);
VectorCopy(p->facets[j].absmaxs, leaf->maxs);
@ -6378,7 +6493,7 @@ static struct bihnode_s *CM_BuildBIH (model_t *mod, cminfo_t *prv)
}
tmpnodes = nodes+1;
CM_BuildBIHNode(nodes, &tmpnodes, leafs, numleafs);
if (tmpnodes != nodes+numnodes)
if (tmpnodes > nodes+numnodes)
Sys_Error("CM_BuildBIH: generated wrong number of nodes");
BZ_Free(leafs); //just for temporary storage so that CM_BuildBIHNode doesn't need to care
return nodes;
@ -6532,9 +6647,8 @@ static trace_t CM_BoxTrace (model_t *mod, vec3_t start, vec3_t end,
tr.negativedir[j] = (end[j] - start[j]) < 0;
VectorSubtract(end, start, tr.totalmove);
VectorCopy(trace_extents, tr.expand);
tr.startpos = trace_start;
tr.endpos = trace_end;
VectorCopy(trace_start, tr.startpos);
VectorCopy(trace_end, tr.endpos);
tr.trace = &trace_trace;
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2])
CM_RecursiveBIHTest(&tr, prv->bihnodes);

View file

@ -1833,7 +1833,7 @@ static qboolean Q1BSP_EdictInFatPVS(model_t *mod, struct pvscache_s *ent, qbyte
{
int i;
if (ent->num_leafs == MAX_ENT_LEAFS+1)
if (ent->num_leafs < 0)
return true; //it's in too many leafs for us to cope with. Just trivially accept it.
for (i=0 ; i < ent->num_leafs ; i++)
@ -1865,9 +1865,9 @@ static void Q1BSP_RFindTouchedLeafs (model_t *wm, struct pvscache_s *ent, mnode_
if (node->contents == Q1CONTENTS_SOLID)
return;
if (ent->num_leafs >= MAX_ENT_LEAFS)
if ((unsigned)ent->num_leafs >= MAX_ENT_LEAFS)
{
ent->num_leafs = MAX_ENT_LEAFS+1; //too many. mark it as such so we can trivially accept huge mega-big brush models.
ent->num_leafs = -1; //too many. mark it as such so we can trivially accept huge mega-big brush models.
return;
}
@ -2368,10 +2368,10 @@ void BSPX_RenderEnvmaps(model_t *mod)
case TF_BGR24:
pxsize = 3;
break;
case TF_RGBA16F:
case PTI_RGBA16F:
pxsize = 8;
break;
case TF_RGBA32F:
case PTI_RGBA32F:
pxsize = 16;
break;
default: //erk!

View file

@ -2745,7 +2745,7 @@ static void BE_GenPolyBatches(batch_t **batches)
}
void R_HalfLife_GenerateBatches(entity_t *e, batch_t **batches);
void PR_Route_Visualise(void);
void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemode)
void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemode, qbyte *worldpvs)
{
int i;
entity_t *ent;
@ -2755,6 +2755,10 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo
// unsigned int orig_numstrisidx = cl_numstrisidx;
// unsigned int orig_numstrisvert = cl_numstrisvert;
extern cvar_t chase_active; //I fucking hate this cvar. die die die.
extern cvar_t r_ignoreentpvs; //legacy value is 1...
if (r_ignoreentpvs.ival)
worldpvs = NULL;
/*clear the batch list*/
for (i = 0; i < SHADER_SORT_COUNT; i++)
@ -2806,6 +2810,9 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo
}
#endif
if (worldpvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &ent->pvscache, worldpvs))
continue;
switch(ent->rtype)
{
case RT_MODEL:

View file

@ -5559,12 +5559,12 @@ batch_t *GLBE_GetTempBatch(void)
/*called from shadowmapping code*/
#ifdef RTLIGHTS
void GLBE_BaseEntTextures(void)
void GLBE_BaseEntTextures(qbyte *worldpvs)
{
batch_t *batches[SHADER_SORT_COUNT];
batch_t **ob = shaderstate.mbatches;
shaderstate.mbatches = batches;
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode);
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, worldpvs);
GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
GLBE_SelectEntity(&r_worldentity);
shaderstate.mbatches = ob;
@ -6231,7 +6231,7 @@ void GLBE_DrawWorld (batch_t **worldbatches)
}
//memset(batches, 0, sizeof(batches));
BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD);
BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis);
R_GenDlightBatches(batches);
shaderstate.curentity = &r_worldentity;
// if (cl.paused || cls.state < ca_active)

View file

@ -13,6 +13,13 @@ See gl_terrain.h for terminology, networking notes, etc.
#include "gl_terrain.h"
static terrainfuncs_t terrainfuncs;
struct patchvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
};
cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes.");
cvar_t mod_terrain_defaulttexture = CVARD("mod_terrain_defaulttexture", "", "Newly created terrain tiles will use this texture. This should generally be updated by the terrain editor.");
@ -5470,6 +5477,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
vec3_t normal[65536];
vec3_t svector[65536];
vec3_t tvector[65536];
vec4_t rgba[65536];
index_t index[65535];
} *arrays = NULL;
size_t numverts = 0;
@ -5858,6 +5866,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
VectorCopy(br->patch->verts[r2].norm, arrays->normal[r1]);
VectorCopy(br->patch->verts[r2].sdir, arrays->svector[r1]);
VectorCopy(br->patch->verts[r2].tdir, arrays->tvector[r1]);
Vector4Copy(br->patch->verts[r2].rgba, arrays->rgba[r1]);
Vector2Copy(br->patch->verts[r2].tc, arrays->lmcoord[r1]);
}
@ -5891,6 +5900,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
VectorCopy(br->planes[j], arrays->normal[o]);
VectorCopy(br->faces[j].stdir[0], arrays->svector[o]);
VectorCopy(br->faces[j].stdir[1], arrays->tvector[o]);
Vector4Set(arrays->rgba[o], 1.0, 1.0, 1.0, 1.0);
//compute the texcoord planes
s = (DotProduct(arrays->svector[o], arrays->coord[o]) + br->faces[j].stdir[0][3]);
@ -5915,7 +5925,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
if (numverts || numindicies)
{
bb = Z_Malloc(sizeof(*bb) + (sizeof(bb->mesh.xyz_array[0])+sizeof(arrays->texcoord[0])+sizeof(arrays->lmcoord[0])+sizeof(arrays->normal[0])+sizeof(arrays->svector[0])+sizeof(arrays->tvector[0])) * numverts);
bb = Z_Malloc(sizeof(*bb) + (sizeof(bb->mesh.xyz_array[0])+sizeof(arrays->texcoord[0])+sizeof(arrays->lmcoord[0])+sizeof(arrays->normal[0])+sizeof(arrays->svector[0])+sizeof(arrays->tvector[0])+sizeof(arrays->rgba[0])) * numverts);
bb->next = bt->batches;
bt->batches = bb;
bb->lightmap = lmnum;
@ -5926,6 +5936,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
BE_VBO_Data(&ctx, arrays->normal, sizeof(arrays->normal [0])*numverts, &bb->vbo.normals);
BE_VBO_Data(&ctx, arrays->svector, sizeof(arrays->svector [0])*numverts, &bb->vbo.svector);
BE_VBO_Data(&ctx, arrays->tvector, sizeof(arrays->tvector [0])*numverts, &bb->vbo.tvector);
BE_VBO_Data(&ctx, arrays->rgba, sizeof(arrays->rgba [0])*numverts, &bb->vbo.colours[0]);
BE_VBO_Finish(&ctx, arrays->index, sizeof(arrays->index [0])*numindicies, &bb->vbo.indicies, &bb->vbo.vbomem, &bb->vbo.ebomem);
bb->mesh.xyz_array = (vecV_t*)(bb+1);
@ -5940,6 +5951,8 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
memcpy(bb->mesh.snormals_array, arrays->svector, sizeof(*bb->mesh.snormals_array) * numverts);
bb->mesh.tnormals_array = (vec3_t*)(bb->mesh.snormals_array+numverts);
memcpy(bb->mesh.tnormals_array, arrays->tvector, sizeof(*bb->mesh.tnormals_array) * numverts);
bb->mesh.colors4f_array[0] = (vec4_t*)(bb->mesh.tnormals_array+numverts);
memcpy(bb->mesh.colors4f_array[0], arrays->rgba, sizeof(*bb->mesh.colors4f_array[0]) * numverts);
bb->pmesh = &bb->mesh;
bb->mesh.numindexes = numindicies;
@ -6212,7 +6225,7 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
}
static brushes_t *Terr_Patch_Insert(model_t *model, heightmap_t *hm, brushtex_t *patch_tex, int patch_w, int patch_h, vec5_t *patch_v, int stride)
static brushes_t *Terr_Patch_Insert(model_t *model, heightmap_t *hm, brushtex_t *patch_tex, int patch_w, int patch_h, struct patchvert_s *patch_v, int stride)
{
int x, y;
brushes_t brush;
@ -6232,11 +6245,9 @@ static brushes_t *Terr_Patch_Insert(model_t *model, heightmap_t *hm, brushtex_t
{
for (x = 0; x < patch_w; x++)
{
brush.patch->verts[x + y*patch_w].v[0] = patch_v[x][0];
brush.patch->verts[x + y*patch_w].v[1] = patch_v[x][1];
brush.patch->verts[x + y*patch_w].v[2] = patch_v[x][2];
brush.patch->verts[x + y*patch_w].tc[0] = patch_v[x][3];
brush.patch->verts[x + y*patch_w].tc[1] = patch_v[x][4];
VectorCopy(patch_v[x].v, brush.patch->verts[x + y*patch_w].v);
Vector2Copy(patch_v[x].tc, brush.patch->verts[x + y*patch_w].tc);
Vector4Copy(patch_v[x].rgba, brush.patch->verts[x + y*patch_w].rgba);
//brush.patch->verts[x + y*patch_w].norm
//brush.patch->verts[x + y*patch_w].sdir
//brush.patch->verts[x + y*patch_w].tdir
@ -7083,7 +7094,16 @@ void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br)
VFS_PRINTF(file, "\n{");
if (br->patch)
{
VFS_PRINTF(file, "\n\tpatchDef2\n\t{\n\t\t\"%s\"\n\t\t( %.9g %.9g %.9g %.9g %.9g )\n\t\t(\n",
qboolean hasrgba = false;
for (y = 0; y < br->patch->ypoints*br->patch->xpoints; y++)
{
if (br->patch->verts[y].rgba[0] != 1.0 || br->patch->verts[y].rgba[1] != 1.0 || br->patch->verts[y].rgba[2] != 1.0 || br->patch->verts[y].rgba[3] != 1.0)
break;
}
hasrgba = (y < br->patch->ypoints*br->patch->xpoints);
VFS_PRINTF(file, "\n\tpatchDef%s\n\t{\n\t\t\"%s\"\n\t\t( %.9g %.9g %.9g %.9g %.9g )\n\t\t(\n",
hasrgba?"WS":"2",
br->patch->tex?br->patch->tex->shadername:"",
0.0/*xoffset*/,
0.0/*yoffset*/,
@ -7095,11 +7115,20 @@ void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br)
VFS_PRINTF(file, "\t\t\t(\n");
for (x = 0; x < br->patch->xpoints; x++)
{
VFS_PRINTF(file, "\t\t\t\t( %.9g %.9g %.9g %.9g %.9g )\n", br->patch->verts[x + y*br->patch->xpoints].v[0],
br->patch->verts[x + y*br->patch->xpoints].v[1],
br->patch->verts[x + y*br->patch->xpoints].v[2],
br->patch->verts[x + y*br->patch->xpoints].tc[0],
br->patch->verts[x + y*br->patch->xpoints].tc[1]);
const char *fmt;
if (hasrgba)
fmt = "\t\t\t\t( %.9g %.9g %.9g %.9g %.9g %.9g %.9g %.9g %.9g )\n";
else
fmt = "\t\t\t\t( %.9g %.9g %.9g %.9g %.9g )\n"; //q3 compat.
VFS_PRINTF(file, fmt, br->patch->verts[x + y*br->patch->xpoints].v[0],
br->patch->verts[x + y*br->patch->xpoints].v[1],
br->patch->verts[x + y*br->patch->xpoints].v[2],
br->patch->verts[x + y*br->patch->xpoints].tc[0],
br->patch->verts[x + y*br->patch->xpoints].tc[1],
br->patch->verts[x + y*br->patch->xpoints].rgba[0],
br->patch->verts[x + y*br->patch->xpoints].rgba[1],
br->patch->verts[x + y*br->patch->xpoints].rgba[2],
br->patch->verts[x + y*br->patch->xpoints].rgba[3]);
}
VFS_PRINTF(file, "\t\t\t)\n");
}
@ -7329,7 +7358,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
//patch info
brushtex_t *patch_tex=NULL;
int patch_w=0, patch_h=0;
vec5_t patch_v[64][64];
struct patchvert_s patch_v[64][64];
#ifdef RUNTIMELIGHTING
hm->entsdirty = true;
@ -7451,9 +7480,10 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
continue;
}
}
else if (inbrush && !strcmp(token, "patchDef2"))
else if (inbrush && (!strcmp(token, "patchDef2") || !strcmp(token, "patchDefWS")))
{
int x, y;
qboolean parsergba = !strcmp(token, "patchDefWS"); //fancy alternative with rgba colours per control point
if (numplanes || patch_tex)
{
Con_Printf(CON_ERROR "%s: mixed patch+planes\n", mod->name);
@ -7491,15 +7521,34 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
while (!strcmp(token, "("))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x][0] = atof(token);
patch_v[y][x].v[0] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x][1] = atof(token);
patch_v[y][x].v[1] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x][2] = atof(token);
patch_v[y][x].v[2] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x][3] = atof(token);
patch_v[y][x].tc[0] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x][4] = atof(token);
patch_v[y][x].tc[1] = atof(token);
if (parsergba)
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x].rgba[0] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x].rgba[1] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x].rgba[2] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
patch_v[y][x].rgba[3] = atof(token);
}
else
{ //no data provided, use default values.
patch_v[y][x].rgba[0] =
patch_v[y][x].rgba[1] =
patch_v[y][x].rgba[2] =
patch_v[y][x].rgba[3] = 1.0;
}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}

View file

@ -1938,7 +1938,7 @@ void GLR_RenderView (void)
if (r_hdr_framebuffer.ival && !(vid.flags & VID_FP16)) //primary use of this cvar is to fix q3shader overbrights (so bright lightmaps can oversaturate then drop below 1 by modulation with the lightmap
forcedfb = true;
if (vid_hardwaregamma.ival == 4 && (v_gamma.value != 1 || v_contrast.value != 1 || v_brightness.value != 0))
if (vid_hardwaregamma.ival == 4 && (v_gamma.value != 1 || v_contrast.value != 1 || v_contrastboost.value != 1|| v_brightness.value != 0))
r_refdef.flags |= RDF_SCENEGAMMA;
}
@ -2158,7 +2158,7 @@ void GLR_RenderView (void)
{
if (r_refdef.flags & RDF_SCENEGAMMA)
{
R2D_ImageColours (v_gamma.value, v_contrast.value, v_brightness.value, 1);
R2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value);
sourcetex = R_RenderPostProcess (sourcetex, RDF_SCENEGAMMA, scenepp_gamma, "rt/$gammaed");
R2D_ImageColours (1, 1, 1, 1);
}

View file

@ -556,6 +556,15 @@ static void Shader_ParseVector(shader_t *shader, char **ptr, vec3_t v)
else
bracket = false;
if (!strncmp(token, "0x", 2))
{ //0xRRGGBB
unsigned int hex = strtoul(token, NULL, 0);
v[0] = ((hex>>16)&255)/255.;
v[1] = ((hex>> 8)&255)/255.;
v[2] = ((hex>> 0)&255)/255.;
return;
}
v[0] = atof ( token );
token = Shader_ParseString ( ptr );
@ -1422,7 +1431,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
if (type)
*type++ = 0;
else
type = "sampler2D";
type = "2D";
if (idx)
{
*idx++ = 0;
@ -1434,7 +1443,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
prog->numsamplers = i+1;
//I really want to use layout(binding = %i) here, but its specific to the glsl version (which we don't really know yet)
Q_strlcatfz(prescript, &offset, sizeof(prescript), "#define s_%s s_t%u\nuniform %s s_%s;\n", token, i, type, token);
Q_strlcatfz(prescript, &offset, sizeof(prescript), "#define s_%s s_t%u\nuniform %s%s s_%s;\n", token, i, strncmp(type, "sampler", 7)?"sampler":"", type, token);
}
else
{
@ -6738,10 +6747,11 @@ static qboolean Shader_ReadShaderTerms(parsestate_t *ps, struct scondinfo_s *con
{
if (!Q_stricmp (token, shadermacros[i].name))
{
#define SHADER_MACRO_ARGS 6
#define SHADER_MACRO_ARGS 8
int argn = 0;
char *oldptr;
char arg[SHADER_MACRO_ARGS][256];
char tmp[4096], *out, *in;
//parse args until the end of the line
while (ps->ptr)
{
@ -6756,8 +6766,38 @@ static qboolean Shader_ReadShaderTerms(parsestate_t *ps, struct scondinfo_s *con
argn++;
}
}
for(out = tmp, in = shadermacros[i].body; *in; )
{
if (out == tmp+countof(tmp)-1)
break;
if (*in == '%' && in[1] == '%')
in++; //skip doubled up percents
else if (*in == '%')
{ //expand an arg
char *e;
int i = strtol(in+1, &e, 0);
if (e != in+1)
{
i--;
if (i >= 0 && i < countof(arg))
{
for (in = arg[i]; *in; )
{
if (out == tmp+countof(tmp)-1)
break;
*out++ = *in++;
}
in = e;
continue;
}
}
}
*out++ = *in++;
}
*out = 0;
oldptr = ps->ptr;
ps->ptr = shadermacros[i].body;
ps->ptr = tmp;
Shader_ReadShaderTerms(ps, cond);
ps->ptr = oldptr;
return true;

View file

@ -70,7 +70,7 @@ extern cvar_t r_shadow_shadowmapping_bias;
cvar_t r_sun_dir = CVARD ("r_sun_dir", "0.2 0.5 0.8", "Specifies the direction that crepusular rays appear along");
cvar_t r_sun_colour = CVARFD ("r_sun_colour", "0 0 0", CVAR_ARCHIVE, "Specifies the colour of sunlight that appears in the form of crepuscular rays.");
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour);
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour, qbyte *pvs);
static pvsbuffer_t lvisb, lvisb2;
@ -2237,7 +2237,7 @@ static void Sh_LightFrustumPlanes(dlight_t *l, vec3_t axis[3], vec4_t *planes, i
//culling for the face happens in the caller.
//these faces should thus match Sh_LightFrustumPlanes
static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowmesh_t *smesh, int face, int smsize, float proj[16])
static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowmesh_t *smesh, int face, int smsize, float proj[16], qbyte *lightpvs)
{
vec3_t t1,t2,t3;
texture_t *tex;
@ -2379,7 +2379,7 @@ static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowm
break;
#ifdef GLQUAKE
case QR_OPENGL:
GLBE_BaseEntTextures();
GLBE_BaseEntTextures(lightpvs);
if (lighttype & LSHADER_ORTHO)
qglDisable(GL_DEPTH_CLAMP_ARB);
@ -2387,17 +2387,17 @@ static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowm
#endif
#ifdef D3D9QUAKE
case QR_DIRECT3D9:
D3D9BE_BaseEntTextures();
D3D9BE_BaseEntTextures(lightpvs);
break;
#endif
#ifdef D3D11QUAKE
case QR_DIRECT3D11:
D3D11BE_BaseEntTextures();
D3D11BE_BaseEntTextures(lightpvs);
break;
#endif
#ifdef VKQUAKE
case QR_VULKAN:
VKBE_BaseEntTextures();
VKBE_BaseEntTextures(lightpvs);
break;
#endif
}
@ -2546,7 +2546,7 @@ qboolean Sh_GenShadowMap (dlight_t *l, int lighttype, vec3_t axis[3], qbyte *lvi
if (sidevisible & (1u<<f))
{
RQuantAdd(RQUANT_SHADOWSIDES, 1);
Sh_GenShadowFace(l, axis, lighttype, smesh, f, smsize, r_refdef.m_projection_std);
Sh_GenShadowFace(l, axis, lighttype, smesh, f, smsize, r_refdef.m_projection_std, lvis);
}
}
@ -2813,7 +2813,7 @@ static void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, vec3_t axis[3], qb
BE_SelectEntity(&r_worldentity);
BE_SelectMode(BEM_LIGHT);
Sh_DrawEntLighting(l, colour);
Sh_DrawEntLighting(l, colour, vvis);
}
@ -2822,7 +2822,7 @@ static void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, vec3_t axis[3], qb
draws faces facing the light
Note: Backend mode must have been selected in advance, as must the light to light from
*/
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour)
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour, qbyte *pvs)
{
int tno;
texture_t *tex;
@ -2856,22 +2856,22 @@ static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour)
break;
#ifdef GLQUAKE
case QR_OPENGL:
GLBE_BaseEntTextures();
GLBE_BaseEntTextures(pvs);
break;
#endif
#ifdef VKQUAKE
case QR_VULKAN:
VKBE_BaseEntTextures();
VKBE_BaseEntTextures(pvs);
break;
#endif
#ifdef D3D9QUAKE
case QR_DIRECT3D9:
D3D9BE_BaseEntTextures();
D3D9BE_BaseEntTextures(pvs);
break;
#endif
#ifdef D3D11QUAKE
case QR_DIRECT3D11:
D3D11BE_BaseEntTextures();
D3D11BE_BaseEntTextures(pvs);
break;
#endif
}
@ -3313,7 +3313,7 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3],
//end stencil writing.
BE_SelectMode(BEM_LIGHT);
Sh_DrawEntLighting(dl, colour);
Sh_DrawEntLighting(dl, colour, vvis);
qglDisable(GL_STENCIL_TEST);
qglStencilFunc( GL_ALWAYS, 0, ~0 );
}
@ -3469,7 +3469,7 @@ static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, vec3_t axis[3],
BE_SelectDLight(dl, colour, axis, dl->fov?LSHADER_SPOT:LSHADER_STANDARD);
BE_SelectMode(BEM_LIGHT);
Sh_DrawEntLighting(dl, colour);
Sh_DrawEntLighting(dl, colour, vvis);
}
void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)

View file

@ -277,6 +277,8 @@ typedef struct
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
vec3_t norm;
vec3_t sdir;
vec3_t tdir;

View file

@ -7489,7 +7489,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
//this shader is applies gamma/contrast/brightness to the source image, and dumps it out.
"varying vec2 tc;\n"
"varying vec4 vc;\n"
"varying vec4 vc; //gamma, contrast, brightness, contrastboost\n"
"#ifdef VERTEX_SHADER\n"
"attribute vec2 v_texcoord;\n"
@ -7504,7 +7504,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#ifdef FRAGMENT_SHADER\n"
"void main ()\n"
"{\n"
"gl_FragColor = pow(texture2D(s_t0, tc) * vc.g, vec4(vc.r)) + vc.b;\n"
"vec3 t = texture2D(s_t0, tc).rgb;\n"
"t = vc.a * t/((vc.a-1)*t + 1);\n"
"gl_FragColor = vec4(pow(t, vec3(vc.r))*vc.g + vc.b, 1.0);\n"
"}\n"
"#endif\n"
},

View file

@ -990,9 +990,9 @@ void GLBE_PolyOffsetStencilShadow(qboolean foobar);
void GLBE_PolyOffsetStencilShadow(void);
#endif
//Called from shadowmapping code into backend
void GLBE_BaseEntTextures(void);
void D3D9BE_BaseEntTextures(void);
void D3D11BE_BaseEntTextures(void);
void GLBE_BaseEntTextures(qbyte *worldpvs);
void D3D9BE_BaseEntTextures(qbyte *worldpvs);
void D3D11BE_BaseEntTextures(qbyte *worldpvs);
//prebuilds shadow volumes
void Sh_PreGenerateLights(void);
//Draws lights, called from the backend

View file

@ -12513,21 +12513,35 @@ void PR_DumpPlatform_f(void)
{"VF_SCREENPSIZE", "const float", CS|MENU, D("Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect)."), VF_SCREENPSIZE},
{"VF_VIEWENTITY", "const float", CS, D("Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too."), VF_VIEWENTITY},
{"VF_RT_DESTCOLOUR", "const float", CS|MENU, D("The texture name to write colour info into, this includes both 3d and 2d drawing.\nAdditional arguments are: format (rgba8=1,rgba16f=2,rgba32f=3), sizexy.\nWritten to by both 3d and 2d rendering.\nNote that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour."), VF_RT_DESTCOLOUR0},
// {"VF_RT_DESTCOLOUR1", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR1},
// {"VF_RT_DESTCOLOUR2", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR2},
// {"VF_RT_DESTCOLOUR3", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR3},
{"VF_RT_DESTCOLOUR", "const float", CS|MENU, D("The texture name to write colour info into, this includes both 3d and 2d drawing.\nAdditional arguments are: format (IMGFMT_*), sizexy.\nWritten to by both 3d and 2d rendering.\nNote that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour."), VF_RT_DESTCOLOUR0},
{"VF_RT_DESTCOLOUR1", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR1},
{"VF_RT_DESTCOLOUR2", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR2},
{"VF_RT_DESTCOLOUR3", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR3},
// {"VF_RT_DESTCOLOUR4", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR4},
// {"VF_RT_DESTCOLOUR5", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR5},
// {"VF_RT_DESTCOLOUR6", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR6},
// {"VF_RT_DESTCOLOUR7", "const float", CS|MENU, D("Like VF_RT_DESTCOLOUR, for multiple render targets."), VF_RT_DESTCOLOUR7},
{"VF_RT_SOURCECOLOUR", "const float", CS|MENU, D("The texture name to use with shaders that specify a $sourcecolour map."), VF_RT_SOURCECOLOUR},
{"VF_RT_DEPTH", "const float", CS|MENU, D("The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (16bit=4,24bit=5,32bit=6), sizexy."), VF_RT_DEPTH},
{"VF_RT_DEPTH", "const float", CS|MENU, D("The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (IMGFMT_D*), sizexy."), VF_RT_DEPTH},
{"VF_RT_RIPPLE", "const float", CS|MENU, D("The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy."), VF_RT_RIPPLE},
{"VF_ENVMAP", "const float", CS|MENU, D("The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything."), VF_ENVMAP},
{"VF_USERDATA", "const float", CS|MENU, D("Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform."), VF_USERDATA},
{"VF_SKYROOM_CAMERA", "const float", CS, D("Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes)."), VF_SKYROOM_CAMERA},
{"IMGFMT_R8G8B8A8", "const float", CS|MENU, D("Typical 32bit rgba pixel format."), 1},
{"IMGFMT_R16G16B16A16F","const float", CS|MENU, D("Half-Float pixel format. Requires gl3 support."), 2},
{"IMGFMT_R32G32B32A32F","const float", CS|MENU, D("Regular Float pixel format. Requires gl3 support."), 3},
{"IMGFMT_D16", "const float", CS|MENU, D("16-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*."), 4},
{"IMGFMT_D24", "const float", CS|MENU, D("24-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*."), 5},
{"IMGFMT_D32", "const float", CS|MENU, D("32-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*."), 6},
{"IMGFMT_R8", "const float", CS|MENU, D("Single channel red-only 8bit pixel format."), 7},
{"IMGFMT_R16F", "const float", CS|MENU, D("Single channel red-only Half-Float pixel format. Requires gl3 support."), 8},
{"IMGFMT_R32F", "const float", CS|MENU, D("Single channel red-only Float pixel format. Requires gl3 support."), 9},
{"IMGFMT_A2B10G10R10", "const float", CS|MENU, D("Packed 32-bit packed 10-bit colour pixel format. Requires gl3 support."), 10},
{"IMGFMT_R5G6B5", "const float", CS|MENU, D("Packed 16-bit colour pixel format."), 11},
{"IMGFMT_R4G4B4A4", "const float", CS|MENU, D("Packed 16-bit colour pixel format, with alpha"), 12},
{"IMGFMT_R8G8", "const float", CS|MENU, D("16-bit two-channel pixel format."), 13},
{"RF_VIEWMODEL", "const float", CS, D("Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob."), CSQCRF_VIEWMODEL},
{"RF_EXTERNALMODEL", "const float", CS, D("Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible."), CSQCRF_EXTERNALMODEL},
{"RF_DEPTHHACK", "const float", CS|MENU, D("Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls."), CSQCRF_DEPTHHACK},
@ -12751,6 +12765,7 @@ void PR_DumpPlatform_f(void)
VFS_PRINTF(f, "#pragma warning enable F302 /*uninitialised locals. They usually default to 0 in qc (except in recursive functions), but its still probably a bug*/\n");
// VFS_PRINTF(f, "#pragma warning %s F308 /*Optional arguments differ on redeclaration.*/\n", (targ & ID1)?"disable":"enable");
#ifdef HEXEN2
if ((targ&ALL) == H2)
{
if (targ&FTE)
@ -12759,6 +12774,7 @@ void PR_DumpPlatform_f(void)
VFS_PRINTF(f, "#pragma target H2\n");
}
else
#endif
{
if (targ&FTE)
VFS_PRINTF(f, "#pragma target FTE\n");

View file

@ -3,7 +3,7 @@
//this shader is applies gamma/contrast/brightness to the source image, and dumps it out.
varying vec2 tc;
varying vec4 vc;
varying vec4 vc; //gamma, contrast, brightness, contrastboost
#ifdef VERTEX_SHADER
attribute vec2 v_texcoord;
@ -18,6 +18,8 @@ void main ()
#ifdef FRAGMENT_SHADER
void main ()
{
gl_FragColor = pow(texture2D(s_t0, tc) * vc.g, vec4(vc.r)) + vc.b;
vec3 t = texture2D(s_t0, tc).rgb;
t = vc.a * t/((vc.a-1)*t + 1);
gl_FragColor = vec4(pow(t, vec3(vc.r))*vc.g + vc.b, 1.0);
}
#endif

View file

@ -5785,10 +5785,10 @@ void VKBE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int
#ifdef RTLIGHTS
//FIXME: needs context for threading
void VKBE_BaseEntTextures(void)
void VKBE_BaseEntTextures(qbyte *scenepvs)
{
batch_t *batches[SHADER_SORT_COUNT];
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode);
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, scenepvs);
VKBE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
VKBE_SelectEntity(&r_worldentity);
}
@ -6240,7 +6240,7 @@ void VKBE_DrawWorld (batch_t **worldbatches)
shaderstate.curdlight = NULL;
//fixme: figure out some way to safely orphan this data so that we can throw the rest to a worker.
BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD);
BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis);
BE_UploadLightmaps(false);
if (r_refdef.scenevis)

View file

@ -465,7 +465,7 @@ void VKBE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);
void VKBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);
void VKBE_VBO_Destroy(vboarray_t *vearray, void *mem);
void VKBE_Scissor(srect_t *rect);
void VKBE_BaseEntTextures(void);
void VKBE_BaseEntTextures(qbyte *scenepvs);
struct vk_shadowbuffer;
struct vk_shadowbuffer *VKBE_GenerateShadowBuffer(vecV_t *verts, int numverts, index_t *indicies, int numindicies, qboolean istemp);