first public attempt at pbr.

update infoblobs to be slightly more self-contained (still not finalised).
q3ui can now change audio volumes.
linearise 16bit srgb textures as required.
code can now potentially support >256 bones. disabled until the stack overflows are fixed...
remap bone indexes where required, for a 10-fold speedup on models with otherwise-too-high bone counts
gltf loader updates, primarily shader changes, for better conformance.
shaders can now specify whether a texture should be treated as srgb or not.
implement serverside download queue for ezquake/legacy clients downloading multiple demos. fte clients should never need to use this (would break total download size display).
some work towards threading shader loading.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5430 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2019-03-12 05:04:27 +00:00
parent d6d8d4c695
commit 5e7688a590
41 changed files with 1902 additions and 882 deletions

View File

@ -3612,7 +3612,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
VectorCopy(snew->angles, le->newangle);
//fixme: should be oldservertime
le->orglerpdeltatime = servertime-le->orglerpstarttime;
le->orglerpdeltatime = bound(0.001, servertime-le->orglerpstarttime, cl_lerp_maxinterval.value);
le->orglerpstarttime = servertime;
}

View File

@ -1922,7 +1922,7 @@ static void CL_SendUserinfoUpdate(void)
size_t bloboffset = cls.userinfosync.keys[0].syncpos;
unsigned int seat = info - cls.userinfo;
size_t blobsize;
const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize);
const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, NULL);
size_t sendsize = blobsize - bloboffset;
const char *s;

View File

@ -5272,6 +5272,41 @@ static void CL_UpdateUserinfo (void)
}
}
static void CL_ParseSetInfoBlob (void)
{
qbyte slot = MSG_ReadByte();
char *key = MSG_ReadString();
size_t keysize;
unsigned int offset = MSG_ReadLong();
qboolean final = !!(offset & 0x80000000);
unsigned short valsize = MSG_ReadShort();
char *val = BZ_Malloc(valsize);
MSG_ReadData(val, valsize);
offset &= ~0x80000000;
key = InfoBuf_DecodeString(key, key+strlen(key), &keysize);
if (slot-- == 0)
InfoBuf_SyncReceive(&cl.serverinfo, key, keysize, val, valsize, offset, final);
else if (slot >= MAX_CLIENTS)
Con_Printf("INVALID SETINFO %i: %s=%s\n", slot, key, val);
else
{
player_info_t *player = &cl.players[slot];
if (offset)
Con_DLPrintf(2,"SETINFO %s: %s+=%s\n", player->name, key, val);
else
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
InfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final);
player->userinfovalid = true;
if (final)
CL_ProcessUserInfo (slot, player);
}
Z_Free(key);
Z_Free(val);
}
/*
==============
CL_SetInfo
@ -5281,67 +5316,27 @@ static void CL_ParseSetInfo (void)
{
int slot;
player_info_t *player;
char *temp;
char *key;
char *val;
unsigned int offset;
qboolean final;
size_t keysize;
size_t valsize;
char key[512];
slot = MSG_ReadByte ();
if (slot == 255 && (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS))
{
slot = MSG_ReadByte();
offset = MSG_ReadLong();
final = !!(offset & 0x80000000);
offset &= ~0x80000000;
}
else
{
final = true;
offset = 0;
}
MSG_ReadStringBuffer(key, sizeof(key));
val = MSG_ReadString();
temp = MSG_ReadString();
if (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS)
key = InfoBuf_DecodeString(temp, temp+strlen(temp), &keysize);
else
{
keysize = strlen(temp);
key = Z_StrDup(temp);
}
temp = MSG_ReadString();
if (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS)
val = InfoBuf_DecodeString(temp, temp+strlen(temp), &valsize);
else
{
valsize = strlen(temp);
val = Z_StrDup(temp);
}
if (slot == 255)
InfoBuf_SyncReceive(&cl.serverinfo, key, keysize, val, valsize, offset, final);
else if (slot >= MAX_CLIENTS)
if (slot >= MAX_CLIENTS)
Con_Printf("INVALID SETINFO %i: %s=%s\n", slot, key, val);
else
{
player = &cl.players[slot];
if (offset)
Con_DLPrintf(2,"SETINFO %s: %s+=%s\n", player->name, key, val);
else
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
InfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final);
InfoBuf_SetStarKey(&player->userinfo, key, val);
player->userinfovalid = true;
CL_ProcessUserInfo (slot, player);
}
Z_Free(key);
Z_Free(val);
}
/*
@ -7160,10 +7155,12 @@ void CLQW_ParseServerMessage (void)
case svc_setinfo:
CL_ParseSetInfo ();
break;
case svc_serverinfo:
CL_ServerInfo ();
break;
case svcfte_setinfoblob:
CL_ParseSetInfoBlob();
break;
case svc_download:
CL_ParseDownload (false);

View File

@ -1253,7 +1253,9 @@ static qintptr_t VARGS Plug_Mod_GetPluginModelFuncs(void *offset, quintptr_t mas
#endif
NULL,
Image_GetTexture,
FS_OpenVFS
FS_OpenVFS,
Mod_AccumulateTextureVectors,
Mod_NormaliseTextureVectors,
};
if (VM_LONG(arg[0]) >= sizeof(funcs))
return (qintptr_t)&funcs;

View File

@ -696,7 +696,53 @@ void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font)
}
}
static cvar_t *Cvar_Q3FindVar (const char *var_name)
{
struct {
const char *q3;
const char *fte;
} cvarremaps[] =
{
{"s_musicvolume", "bgmvolume"},
{"r_gamma", "gamma"},
{"s_sdlSpeed", "s_khz"},
{"r_fullscreen", "vid_fullscreen"},
{"r_picmip", "gl_picmip"},
{"r_textureMode", "gl_texturemode"},
{"r_lodBias", "d_lodbias"},
{"r_colorbits", "vid_bpp"},
{"r_dynamiclight", "r_dynamic"},
{"r_finish", "gl_finish"},
// {"r_glDriver", NULL},
// {"r_depthbits", NULL},
// {"r_stencilbits", NULL},
// {"s_compression", NULL},
// {"r_texturebits", NULL},
// {"r_allowExtensions",NULL},
// {"s_useOpenAL", NULL},
// {"sv_running", NULL},
// {"sv_killserver", NULL},
// {"color1", NULL},
// {"in_joystick", NULL},
// {"joy_threshold", NULL},
// {"cl_freelook", NULL},
// {"color1", NULL},
// {"r_availableModes",NULL},
// {"r_mode", NULL},
};
cvar_t *v;
size_t i;
v = Cvar_FindVar(var_name);
if (v)
return v;
for (i = 0; i < countof(cvarremaps); i++)
{
if (!strcmp(cvarremaps[i].q3, var_name))
return Cvar_FindVar(cvarremaps[i].fte);
}
// Con_Printf("Q3 Cvar %s is not known\n", var_name);
return NULL;
}
#define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds.
@ -752,7 +798,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
}
else
{
var = Cvar_FindVar(vname);
var = Cvar_Q3FindVar(vname);
if (var)
Cvar_Set(var, vval); //set it
else
@ -764,7 +810,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
{
cvar_t *var;
char *vname = VM_POINTER(arg[0]);
var = Cvar_FindVar(vname);
var = Cvar_Q3FindVar(vname);
if (var)
VM_FLOAT(ret) = var->value;
else
@ -775,7 +821,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
{
cvar_t *var;
char *vname = VM_POINTER(arg[0]);
var = Cvar_FindVar(vname);
var = Cvar_Q3FindVar(vname);
if (!VM_LONG(arg[2]))
VM_LONG(ret) = 0;
else if (!var)
@ -794,14 +840,14 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
break;
case UI_CVAR_SETVALUE:
Cvar_SetValue(Cvar_FindVar(VM_POINTER(arg[0])), VM_FLOAT(arg[1]));
Cvar_SetValue(Cvar_Q3FindVar(VM_POINTER(arg[0])), VM_FLOAT(arg[1]));
break;
case UI_CVAR_RESET: //cvar reset
{
cvar_t *var;
char *vname = VM_POINTER(arg[0]);
var = Cvar_FindVar(vname);
var = Cvar_Q3FindVar(vname);
if (var)
Cvar_Set(var, var->defaultstr);
}

View File

@ -2975,7 +2975,9 @@ void Con_DrawConsole (int lines, qboolean noback)
playerview_t pv;
entity_t ent;
vec3_t fwd, rgt, up;
vec3_t lightpos = {0, 1, 0};
vec3_t lightpos = {1, 1, 0};
float transforms[12];
float scale;
if (R2D_Flush)
R2D_Flush();
@ -3003,15 +3005,14 @@ void Con_DrawConsole (int lines, qboolean noback)
VectorClear(r_refdef.viewangles);
r_refdef.viewangles[0] = 20;
r_refdef.viewangles[1] = realtime * 90;
// r_refdef.viewangles[1] = realtime * 90;
AngleVectors(r_refdef.viewangles, fwd, rgt, up);
VectorScale(fwd, -64, r_refdef.vieworg);
memset(&ent, 0, sizeof(ent));
ent.scale = 1;
// ent.angles[1] = realtime*45;//mods->yaw;
ent.angles[1] = realtime*90;//mods->yaw;
// ent.angles[0] = realtime*23.4;//mods->pitch;
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
@ -3020,8 +3021,16 @@ void Con_DrawConsole (int lines, qboolean noback)
ent.model = model;
if (!ent.model)
return; //panic!
//ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];
ent.scale = 1;
scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2]));
scale = scale?64.0/scale:1;
ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];
Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1);
VectorScale(ent.axis[0], scale, ent.axis[0]);
VectorScale(ent.axis[1], scale, ent.axis[1]);
VectorScale(ent.axis[2], scale, ent.axis[2]);
/*if (strstr(model->name, "player"))
{
ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1);
@ -3042,11 +3051,49 @@ void Con_DrawConsole (int lines, qboolean noback)
ent.framestate.g[FS_REG].endbone = 0x7fffffff;
// ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname));
VectorSet(ent.glowmod, 1,1,1);
ent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66;
ent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33;
V_ApplyRefdef();
if (ent.model->camerabone>0 && Mod_GetTag(ent.model, ent.model->camerabone, &ent.framestate, transforms))
{
VectorClear(ent.origin);
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
VectorInverse(ent.axis[1]);
scale = 1;
{
vec3_t fwd, up;
float camera[12], et[12] = {
ent.axis[0][0], ent.axis[1][0], ent.axis[2][0], ent.origin[0],
ent.axis[0][1], ent.axis[1][1], ent.axis[2][1], ent.origin[1],
ent.axis[0][2], ent.axis[1][2], ent.axis[2][2], ent.origin[2],
};
R_ConcatTransforms((void*)et, (void*)transforms, (void*)camera);
VectorSet(fwd, camera[2], camera[6], camera[10]);
VectorNegate(fwd, fwd);
VectorSet(up, camera[1], camera[5], camera[9]);
VectorSet(r_refdef.vieworg, camera[3], camera[7], camera[11]);
VectorAngles(fwd, up, r_refdef.viewangles, false);
}
}
else
{
ent.angles[1] = realtime*90;//mods->yaw;
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
VectorScale(ent.axis[0], scale, ent.axis[0]);
VectorScale(ent.axis[1], -scale, ent.axis[1]);
VectorScale(ent.axis[2], scale, ent.axis[2]);
}
ent.scale = scale;
VectorNormalize(lightpos);
ent.light_dir[0] = DotProduct(lightpos, ent.axis[0]);
ent.light_dir[1] = DotProduct(lightpos, ent.axis[1]);

View File

@ -7602,6 +7602,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
switch(nf)
{
case PTI_R8:
case PTI_L8:
for (i = 0; i < m; i++)
((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));
@ -7611,6 +7612,19 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
for (i = 0; i < m; i+=2)
((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));
break;
case PTI_R16:
for (i = 0; i < m; i+=4)
((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff));
break;
case PTI_RGBA16:
m*=4;
for (i = 0; i < m; i+=4)
{
((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff));
((unsigned short*)rgbadata)[i+1] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+1] * (1.0/0xffff));
((unsigned short*)rgbadata)[i+2] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+2] * (1.0/0xffff));
}
break;
case PTI_RGBA8:
case PTI_RGBX8:
case PTI_BGRA8:
@ -7627,7 +7641,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_BC1_RGBA:
case PTI_BC2_RGBA:
case PTI_BC3_RGBA:
//FIXME: bc1/2/3 has two leading 16bit values per block.
//FIXME: bc1/2/3 has two leading 16bit 565 values per block.
default:
//these formats are weird. we can't just fiddle with the rgbdata
//FIXME: etc2 has all sorts of weird encoding tables...
@ -8656,7 +8670,7 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns
qboolean dontposttoworker = (flags & (IF_NOWORKER | IF_LOADNOW));
qboolean lowpri = (flags & IF_LOWPRIORITY);
// qboolean highpri = (flags & IF_HIGHPRIORITY);
qboolean highpri = (flags & IF_HIGHPRIORITY);
flags &= ~(IF_LOADNOW | IF_LOWPRIORITY | IF_HIGHPRIORITY);
#ifdef LOADERTHREAD
@ -8782,7 +8796,9 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns
}
else
#endif
if (lowpri)
if (highpri)
COM_InsertWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
else if (lowpri)
COM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);
else
COM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);

View File

@ -3254,7 +3254,6 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
VectorScale(fwd, -mods->dist, r_refdef.vieworg);
memset(&ent, 0, sizeof(ent));
ent.scale = 1;
// ent.angles[1] = realtime*45;//mods->yaw;
// ent.angles[0] = realtime*23.4;//mods->pitch;
@ -3266,8 +3265,18 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
ent.model = Mod_ForName(mods->modelname, MLV_WARN);
if (!ent.model)
return; //panic!
ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];
ent.scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2]));
ent.scale = ent.scale?64.0/ent.scale:1;
ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5;// + ent.model->mins[2];
ent.origin[2] *= ent.scale;
Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1);
VectorSet(ent.glowmod, 1, 1, 1);
// VectorScale(ent.axis[0], ent.scale, ent.axis[0]);
// VectorScale(ent.axis[1], ent.scale, ent.axis[1]);
// VectorScale(ent.axis[2], ent.scale, ent.axis[2]);
// ent.scale = 1;
if (strstr(mods->modelname, "player"))
{
ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1);
@ -3311,6 +3320,9 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
VectorCopy(tr.endpos, lightpos);
}
*/
lightpos[0] = sin(realtime*0.1);
lightpos[1] = cos(realtime*0.1);
lightpos[2] = 0;
VectorNormalize(lightpos);
ent.light_dir[0] = DotProduct(lightpos, ent.axis[0]);
@ -3619,12 +3631,14 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
"body: %i\n"
"geomset: %i %i%s\n"
"numverts: %i\nnumtris: %i\n"
"numbones: %i\n"
, ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2],
contents,
inf->csurface.flags,
inf->surfaceid,
inf->geomset>=MAX_GEOMSETS?-1:inf->geomset, inf->geomid, inf->geomset>=MAX_GEOMSETS?" (always)":"",
inf->numverts, inf->numindexes/3
inf->numverts, inf->numindexes/3,
inf->numbones
)
, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);
}
@ -3709,7 +3723,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
shader->defaulttextures->base = skin->upperoverlay; //diffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb
break;
case 4:
t = "LopwerMap";
t = "LowerMap";
shader->defaulttextures->base = skin->loweroverlay; //diffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb
break;
case 5:

View File

@ -1544,30 +1544,6 @@ static void CSQC_PolyFlush(void)
csqc_poly_shader = NULL;
}
static void Shader_PolygonShader(const char *shortname, shader_t *s, const void *args)
{
Shader_DefaultScript(shortname, s,
"{\n"
"if $lpp\n"
"program lpp_skin\n"
"else\n"
"program defaultskin#NONORMALS\n"
"endif\n"
"{\n"
"map $diffuse\n"
"rgbgen vertex\n"
"alphagen vertex\n"
"}\n"
"{\n"
"map $fullbright\n"
"blendfunc add\n"
"}\n"
"}\n"
);
if (!s->defaulttextures->base && (s->flags & SHADER_HASDIFFUSE))
R_BuildDefaultTexnums(NULL, s, 0);
}
static shader_t *PR_R_PolygonShader(const char *shadername, qboolean twod)
{
extern shader_t *shader_draw_fill_trans;
@ -3961,7 +3937,7 @@ static void QCBUILTIN PF_cs_serverkeyblob (pubprogfuncs_t *prinst, struct global
}
ptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr);
blob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize);
blob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize, NULL);
if (qcptr)
{
@ -4054,7 +4030,7 @@ static void QCBUILTIN PF_cs_getplayerkeyblob (pubprogfuncs_t *prinst, struct glo
else
{
size_t blobsize = 0;
const char *blob = InfoBuf_BlobForKey(&cl.players[pnum].userinfo, keyname, &blobsize);
const char *blob = InfoBuf_BlobForKey(&cl.players[pnum].userinfo, keyname, &blobsize, NULL);
if (qcptr)
{

View File

@ -355,6 +355,7 @@ typedef enum
WG_COUNT = 2 //main and loaders
} wgroup_t;
void COM_AddWork(wgroup_t 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_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);
qboolean COM_HasWork(void);
void COM_WorkerFullSync(void);
void COM_DestroyWorkerThread(void);

View File

@ -436,7 +436,6 @@ mpic_t *R2D_SafeCachePic (const char *path)
mpic_t *R2D_SafePicFromWad (const char *name)
{
void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs);
shader_t *s;
if (!qrenderer)
return NULL;

View File

@ -892,6 +892,7 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int
int i;
vec3_t ts, te;
physent_t *pe;
model_t *mod;
int result=0;
vec3_t axis[3];
@ -906,7 +907,8 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int
pe = &pmove.physents[i];
if (pe->nonsolid)
continue;
if (pe->model && pe->model->loadstate == MLS_LOADED)
mod = pe->model;
if (mod && mod->loadstate == MLS_LOADED && mod->funcs.NativeTrace)
{
VectorSubtract(start, pe->origin, ts);
VectorSubtract(end, pe->origin, te);
@ -914,10 +916,10 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int
{
AngleVectors(pe->angles, axis[0], axis[1], axis[2]);
VectorNegate(axis[1], axis[1]);
pe->model->funcs.NativeTrace(pe->model, 0, PE_FRAMESTATE, axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);
mod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);
}
else
pe->model->funcs.NativeTrace(pe->model, 0, PE_FRAMESTATE, NULL, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);
mod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, NULL, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);
if (trace.fraction<1)
{
if (bestfrac > trace.fraction)

View File

@ -402,40 +402,45 @@ void R_RenderDlights (void);
enum imageflags
{
/*warning: many of these flags only apply the first time it is requested*/
IF_CLAMP = 1<<0, //disable texture coord wrapping.
IF_NOMIPMAP = 1<<1, //disable mipmaps.
IF_NEAREST = 1<<2, //force nearest
IF_LINEAR = 1<<3, //force linear
IF_UIPIC = 1<<4, //subject to texturemode2d
//IF_DEPTHCMD=1<<5, //Reserved for d3d11
IF_SRGB = 1<<6, //texture data is srgb
IF_CLAMP = 1<<0, //disable texture coord wrapping.
IF_NOMIPMAP = 1<<1, //disable mipmaps.
IF_NEAREST = 1<<2, //force nearest
IF_LINEAR = 1<<3, //force linear
IF_UIPIC = 1<<4, //subject to texturemode2d
//IF_DEPTHCMD = 1<<5, //Reserved for d3d11
IF_SRGB = 1<<6, //texture data is srgb (read-as-linear)
/*WARNING: If the above are changed, be sure to change shader pass flags*/
IF_NOPICMIP = 1<<7,
IF_NOALPHA = 1<<8, /*hint rather than requirement*/
IF_NOGAMMA = 1<<9,
IF_3DMAP = 1<<10, /*waning - don't test directly*/
IF_CUBEMAP = 1<<11, /*waning - don't test directly*/
IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/
IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/
IF_MIPCAP = 1<<12,
IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha
IF_NOPICMIP = 1<<7,
IF_NOALPHA = 1<<8, /*hint rather than requirement*/
IF_NOGAMMA = 1<<9, /*do not apply texture-based gamma*/
IF_3DMAP = 1<<10, /*waning - don't test directly*/
IF_CUBEMAP = 1<<11, /*waning - don't test directly*/
IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/
IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/
IF_MIPCAP = 1<<12, //allow the use of d_mipcap
IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha
IF_WORLDTEX = 1<<18, //gl_picmip_world
IF_SPRITETEX = 1<<19, //gl_picmip_sprites
IF_NOSRGB = 1<<20, //ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc.
IF_UNUSED14 = 1<<14, //
IF_UNUSED15 = 1<<15, //
IF_UNUSED16 = 1<<16, //
IF_UNUSED17 = 1<<17, //
IF_PALETTIZE = 1<<21,
IF_NOPURGE = 1<<22,
IF_HIGHPRIORITY = 1<<23,
IF_LOWPRIORITY = 1<<24,
IF_LOADNOW = 1<<25, /*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance*/
IF_NOPCX = 1<<26, /*block pcx format. meaning qw skins can use team colours and cropping*/
IF_TRYBUMP = 1<<27, /*attempt to load _bump if the specified _norm texture wasn't found*/
IF_RENDERTARGET = 1<<28, /*never loaded from disk, loading can't fail*/
IF_EXACTEXTENSION = 1<<29, /*don't mangle extensions, use what is specified and ONLY that*/
IF_NOREPLACE = 1<<30, /*don't load a replacement, for some reason*/
IF_NOWORKER = 1u<<31 /*don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.*/
IF_WORLDTEX = 1<<18, //gl_picmip_world
IF_SPRITETEX = 1<<19, //gl_picmip_sprites
IF_NOSRGB = 1<<20, //ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc.
IF_PALETTIZE = 1<<21, //convert+load it as an RTI_P8 texture for the current palette/colourmap
IF_NOPURGE = 1<<22, //texture is not flushed when no more shaders refer to it (for C code that holds a permanant reference to it - still purged on vid_reloads though)
IF_HIGHPRIORITY = 1<<23, //pushed to start of worker queue instead of end...
IF_LOWPRIORITY = 1<<24, //
IF_LOADNOW = 1<<25, /*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance*/
IF_NOPCX = 1<<26, /*block pcx format. meaning qw skins can use team colours and cropping*/
IF_TRYBUMP = 1<<27, /*attempt to load _bump if the specified _norm texture wasn't found*/
IF_RENDERTARGET = 1<<28, /*never loaded from disk, loading can't fail*/
IF_EXACTEXTENSION = 1<<29, /*don't mangle extensions, use what is specified and ONLY that*/
IF_NOREPLACE = 1<<30, /*don't load a replacement, for some reason*/
IF_NOWORKER = 1u<<31 /*don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.*/
};
#define R_LoadTexture8(id,w,h,d,f,t) Image_GetTexture(id, NULL, f, d, NULL, w, h, t?TF_TRANS8:TF_SOLID8)

View File

@ -74,7 +74,7 @@ int sound_started=0;
cvar_t bgmvolume = CVARAFD( "musicvolume", "0.3", "bgmvolume", CVAR_ARCHIVE,
"Volume level for background music.");
cvar_t volume = CVARFD( "volume", "0.7", CVAR_ARCHIVE,
cvar_t volume = CVARAFD( "volume", "0.7", /*q3*/"s_volume",CVAR_ARCHIVE,
"Main volume level for all engine sound.");
cvar_t nosound = CVARFD( "nosound", "0", CVAR_ARCHIVE,

View File

@ -1011,9 +1011,13 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st
return DEBUG_TRACE_OFF; //whoops
}
if (reason)
Con_Printf("QC Exception: %s\n", reason);
if (!pr_debugger.ival)
{
Con_Printf("Set %s to trace\n", pr_debugger.name);
if (!stepasm && *filename)
Con_Printf("Set %s to trace\n", pr_debugger.name);
if (fatal)
return DEBUG_TRACE_ABORT;
return DEBUG_TRACE_OFF; //get lost

View File

@ -140,7 +140,7 @@ static clampedmodel_t clampedmodel[] = {
void Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms)
void QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms)
{
int i;
const float *v0, *v1, *v2;
@ -215,7 +215,7 @@ void Mod_AccumulateMeshTextureVectors(mesh_t *m)
Mod_AccumulateTextureVectors(m->xyz_array, m->st_array, m->normals_array, m->snormals_array, m->tnormals_array, m->indexes, m->numindexes, false);
}
void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms)
void QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms)
{
int i;
float f;
@ -352,7 +352,7 @@ static void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz
#endif
/*transforms some skeletal vecV_t values*/
static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout)
static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, boneidx_t *bidx, float *weights, float *xyzin, float *fte_restrict xyzout)
{
#if 1
int i, j;
@ -428,7 +428,7 @@ static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qby
}
/*transforms some skeletal vecV_t values*/
static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const qbyte *bidx, float *weights,
static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const boneidx_t *bidx, float *weights,
const float *xyzin, float *fte_restrict xyzout,
const float *normin, float *fte_restrict normout)
{
@ -472,7 +472,7 @@ static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, co
}
/*transforms some skeletal vecV_t values*/
static void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const qbyte *bidx, const float *weights,
static void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const boneidx_t *bidx, const float *weights,
const float *xyzin, float *fte_restrict xyzout,
const float *normin, float *fte_restrict normout,
const float *sdirin, float *fte_restrict sdirout,
@ -708,6 +708,8 @@ struct
entity_t *ent;
#ifdef SKELETALMODELS
boneidx_t *bonemap; //force the renderer to forget the current entity when this changes
float gpubones[MAX_BONES*12]; //temp storage for multi-surface models with too many bones.
float boneposebuffer1[MAX_BONES*12];
float boneposebuffer2[MAX_BONES*12];
skeltype_t bonecachetype;
@ -1325,7 +1327,7 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *fr
static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, galiasinfo_t *inf)
{
qbyte *fte_restrict bidx = inf->ofs_skel_idx[0];
boneidx_t *fte_restrict bidx = inf->ofs_skel_idx[0];
float *fte_restrict weight = inf->ofs_skel_weight[0];
if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE)
@ -1350,7 +1352,7 @@ static void Alias_BuildSkeletalVerts(float *xyzout, framestate_t *framestate, ga
{
float buffer[MAX_BONES*12];
float bufferalt[MAX_BONES*12];
qbyte *fte_restrict bidx = inf->ofs_skel_idx[0];
boneidx_t *fte_restrict bidx = inf->ofs_skel_idx[0];
float *fte_restrict weight = inf->ofs_skel_weight[0];
const float *bonepose = Alias_GetBoneInformation(inf, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES);
@ -1654,11 +1656,11 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
usebones = false;
else if (inf->ofs_skel_xyz && !inf->ofs_skel_weight)
usebones = false;
else if (e->fatness || !inf->ofs_skel_idx || inf->numbones > sh_config.max_gpu_bones)
else if (e->fatness || !inf->ofs_skel_idx || (!inf->mappedbones && inf->numbones > sh_config.max_gpu_bones))
#endif
usebones = false;
if (0)//meshcache.ent == e)
if (meshcache.ent == e)
{
if (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e && usebones == meshcache.usebones)
{
@ -1683,7 +1685,22 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
mesh->boneweights = inf->ofs_skel_weight;
mesh->bones = meshcache.usebonepose;
mesh->numbones = inf->numbones;
}
#ifndef SERVERONLY
if (meshcache.bonemap != inf->bonemap)
{
meshcache.bonemap = inf->bonemap;
BE_SelectEntity(e);
}
if (inf->mappedbones)
{
int i;
for (i = 0; i < inf->mappedbones; i++)
memcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12);
meshcache.vbo.numbones = inf->mappedbones;
meshcache.vbo.bones = meshcache.gpubones;
}
#endif
return false; //don't generate the new vertex positions. We still have them all.
}
if (meshcache.bonegroup != inf->shares_bones)
@ -2002,6 +2019,21 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
mesh->boneweights = inf->ofs_skel_weight;
mesh->bones = meshcache.usebonepose;
mesh->numbones = inf->numbones;
#ifndef SERVERONLY
if (meshcache.bonemap != inf->bonemap)
{
meshcache.bonemap = inf->bonemap;
BE_SelectEntity(e);
}
if (inf->mappedbones)
{
int i;
for (i = 0; i < inf->mappedbones; i++)
memcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12);
meshcache.vbo.numbones = inf->mappedbones;
meshcache.vbo.bones = meshcache.gpubones;
}
#endif
}
#endif
@ -2779,7 +2811,7 @@ void Mod_DestroyMesh(galiasinfo_t *galias)
}
#ifndef SERVERONLY
static void Mod_GenerateMeshVBO(galiasinfo_t *galias)
static void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias)
//vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts)
{
#ifdef NONSKELETALMODELS
@ -2838,10 +2870,64 @@ static void Mod_GenerateMeshVBO(galiasinfo_t *galias)
BE_VBO_Data(&vboctx, galias->ofs_skel_svect, sizeof(*galias->ofs_skel_svect) * galias->numverts, &galias->vbo_skel_svector);
if (galias->ofs_skel_tvect)
BE_VBO_Data(&vboctx, galias->ofs_skel_tvect, sizeof(*galias->ofs_skel_tvect) * galias->numverts, &galias->vbo_skel_tvector);
if (galias->ofs_skel_idx)
BE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum);
if (galias->ofs_skel_weight)
BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight);
if (!galias->mappedbones /*&& galias->numbones > sh_config.max_gpu_bones*/ && galias->ofs_skel_idx)
{ //if we're using gpu bones, then its possible that we're trying to load a model with more bones than the gpu supports
//to work around this (and get performance back), each surface has a gpu->cpu table so that bones not used on a mesh don't cause it to need to use a software fallback
qboolean *seen = alloca(sizeof(*seen) * galias->numbones);
int j, k;
memset(seen, 0, sizeof(*seen) * galias->numbones);
for (j = 0; j < galias->numverts; j++)
for (k = 0; k < 4; k++)
{
if (galias->ofs_skel_weight[j][k])
seen[galias->ofs_skel_idx[j][k]] = true;
}
for (j = 0, k = 0; j < galias->numbones; j++)
{
if (seen[j])
k++;
}
if (k < sh_config.max_gpu_bones)
{ //okay, we can hardware accelerate that.
galias->bonemap = ZG_Malloc(&mod->memgroup, sizeof(*galias->bonemap)*sh_config.max_gpu_bones);
galias->mappedbones = 0;
for (j = 0; j < galias->numbones; j++)
{
if (seen[j])
galias->bonemap[galias->mappedbones++] = j;
}
}
}
if (galias->mappedbones)
{
boneidx_t *remaps = alloca(sizeof(*remaps) * galias->numbones);
bone_vec4_t *bones = alloca(sizeof(*bones) * galias->numverts);
int j, k;
//our remap table is gpu->cpu, but we need cpu->gpu here
for (j = 0; j < galias->numbones; j++)
remaps[j] = 0; //errors.
for (j = 0; j < galias->mappedbones; j++)
remaps[galias->bonemap[j]] = j;
//now remap them all
for (j = 0; j < galias->numverts; j++)
for (k = 0; k < 4; k++)
bones[j][k] = remaps[galias->ofs_skel_idx[j][k]];
//and we can upload
if (galias->ofs_skel_idx)
BE_VBO_Data(&vboctx, bones, sizeof(*bones) * galias->numverts, &galias->vbo_skel_bonenum);
if (galias->ofs_skel_weight)
BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight);
}
else
{
if (galias->ofs_skel_idx)
BE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum);
if (galias->ofs_skel_weight)
BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight);
}
#endif
#ifdef NONSKELETALMODELS
for (i = 0; i < galias->numanimations; i++)
@ -2989,7 +3075,7 @@ void Mod_LoadAliasShaders(model_t *mod)
{
if (numskins < ai->numskins)
numskins = ai->numskins;
Mod_GenerateMeshVBO(ai); //FIXME: shares verts
Mod_GenerateMeshVBO(mod, ai); //FIXME: shares verts
}
for (i = 0; i < numskins; i++)
{
@ -5842,7 +5928,7 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
vecV_t *skel_xyz;
vec3_t *skel_norm, *skel_svect, *skel_tvect;
byte_vec4_t *skel_idx;
bone_vec4_t *skel_idx;
vec4_t *skel_weights;
/*load the psk*/
@ -6127,10 +6213,11 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
skel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*skel_tvect) * num_vtxw);
skel_idx = ZG_Malloc(&mod->memgroup, sizeof(*skel_idx) * num_vtxw);
skel_weights = ZG_Malloc(&mod->memgroup, sizeof(*skel_weights) * num_vtxw);
for (j = 0; j < 4; j++)
skel_idx[i][j] = ~0;
for (i = 0; i < num_vtxw; i++)
{
float t;
*(unsigned int*)skel_idx[i] = ~0;
for (j = 0; j < num_rawweights; j++)
{
if (rawweights[j].pntsindex == vtxw[i].pntsindex)
@ -6945,7 +7032,7 @@ galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, f
}
*/
static qboolean IQM_ImportArray4B(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, byte_vec4_t *fte_restrict out, size_t count, unsigned int maxval)
static qboolean IQM_ImportArray4Bone(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, bone_vec4_t *fte_restrict out, size_t count, unsigned int maxval)
{
size_t i;
unsigned int j;
@ -6953,7 +7040,7 @@ static qboolean IQM_ImportArray4B(const qbyte *fte_restrict base, const struct i
unsigned int fmt = LittleLong(src->format);
unsigned int offset = LittleLong(src->offset);
qboolean invalid = false;
maxval = min(256,maxval); //output is bytes.
maxval = min(MAX_BONES,maxval); //output is bytes.
if (!offset)
{
sz = 0;
@ -7195,7 +7282,7 @@ static const void *IQM_FindExtension(const char *buffer, size_t buffersize, cons
return NULL;
}
static void Mod_CleanWeights(const char *modelname, size_t numverts, vec4_t *oweight, byte_vec4_t *oindex)
static void Mod_CleanWeights(const char *modelname, size_t numverts, vec4_t *oweight, bone_vec4_t *oindex)
{ //some IQMs lack weight values, apparently.
int j, v;
qboolean problemfound = false;
@ -7258,7 +7345,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
vecV_t *opos=NULL;
vec3_t *onorm1=NULL, *onorm2=NULL, *onorm3=NULL;
vec4_t *oweight=NULL;
byte_vec4_t *oindex=NULL;
bone_vec4_t *oindex=NULL;
float *opose=NULL,*oposebase=NULL;
vec2_t *otcoords = NULL;
vec4_t *orgbaf = NULL;
@ -7273,7 +7360,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
galiasanimation_t *fgroup=NULL;
galiasbone_t *bones = NULL;
index_t *idx;
float basepose[12 * MAX_BONES];
qboolean noweights;
frameinfo_t *framegroups;
int numgroups;
@ -7481,10 +7567,10 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
GenMatrixPosQuat3Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat);
if (ijoint[i].parent >= 0)
Matrix3x4_Multiply(mat, &basepose[ijoint[i].parent*12], &basepose[i*12]);
Matrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]);
else
memcpy(&basepose[i*12], mat, sizeof(mat));
Matrix3x4_Invert_Simple(&basepose[i*12], bones[i].inverse);
memcpy(&oposebase[i*12], mat, sizeof(mat));
Matrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse);
}
//pose info (anim)
@ -7526,10 +7612,10 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
GenMatrixPosQuat4Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat);
if (ijoint[i].parent >= 0)
Matrix3x4_Multiply(mat, &basepose[ijoint[i].parent*12], &basepose[i*12]);
Matrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]);
else
memcpy(&basepose[i*12], mat, sizeof(mat));
Matrix3x4_Invert_Simple(&basepose[i*12], bones[i].inverse);
memcpy(&oposebase[i*12], mat, sizeof(mat));
Matrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse);
}
//pose info (anim)
@ -7552,8 +7638,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
}
}
}
//basepose
memcpy(oposebase, basepose, sizeof(float)*12 * h->num_joints);
//now generate the animations.
for (i = 0; i < numgroups; i++)
@ -7724,7 +7808,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
gai[i-1].nextsurf = NULL;
if (!noweights)
{
if (!IQM_ImportArray4B(buffer, &vbone, oindex, h->num_vertexes, h->num_joints))
if (!IQM_ImportArray4Bone(buffer, &vbone, oindex, h->num_vertexes, h->num_joints))
Con_DPrintf(CON_WARNING "Invalid bone indexes detected inside %s\n", mod->name);
IQM_ImportArrayF(buffer, &vweight, (float*)oweight, 4, h->num_vertexes, defaultweight);
Mod_CleanWeights(mod->name, h->num_vertexes, oweight, oindex);

View File

@ -170,8 +170,8 @@ typedef struct galiasinfo_s
struct galiasinfo_s *nextsurf;
#ifdef SKELETALMODELS
// int *bonemap; //some models are horribly complicated, this provides a gpubone->cpubone table, reducing the number of gpu bones needed on a per-mesh basis.
// int mappedbones;
boneidx_t *bonemap; //filled in automatically if our mesh has more gpu bones than we can support
unsigned int mappedbones;
float *baseframeofs; /*non-heirachical*/
int numbones;
@ -181,7 +181,7 @@ typedef struct galiasinfo_s
vec3_t *ofs_skel_norm;
vec3_t *ofs_skel_svect;
vec3_t *ofs_skel_tvect;
byte_vec4_t *ofs_skel_idx;
bone_vec4_t *ofs_skel_idx;
vec4_t *ofs_skel_weight;
vboarray_t vbo_skel_verts;
@ -234,8 +234,8 @@ typedef struct modplugfuncs_s
void *reserved2;
image_t *(QDECL *GetTexture)(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt);
vfsfile_t *(QDECL *OpenVFS)(const char *filename, const char *mode, enum fs_relative relativeto);
void *unused3;
void *unused4;
void (QDECL *AccumulateTextureVectors)(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms);
void (QDECL *NormaliseTextureVectors)(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms);
void *unused5;
void *unused6;
void *unused7;
@ -263,9 +263,9 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam
void Mod_DoCRC(model_t *mod, char *buffer, int buffersize);
void Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms);
void QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms);
void Mod_AccumulateMeshTextureVectors(mesh_t *mesh);
void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms);
void QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms);
void R_Generate_Mesh_ST_Vectors(mesh_t *mesh);
#ifdef __cplusplus

View File

@ -5209,6 +5209,40 @@ qboolean COM_HasWork(void)
}
return false;
}
void COM_InsertWork(wgroup_t 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 (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror))
{
func(ctx, data, a, b);
return;
}
//build the work
work = Z_Malloc(sizeof(*work));
work->func = func;
work->ctx = ctx;
work->data = data;
work->a = a;
work->b = b;
//queue it (fifo)
Sys_LockConditional(com_workercondition[tg]);
work->next = com_work_head[tg];
if (!com_work_tail[tg])
com_work_tail[tg] = work;
com_work_head[tg] = work;
// Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?");
Sys_ConditionSignal(com_workercondition[tg]);
Sys_UnlockConditional(com_workercondition[tg]);
}
void COM_AddWork(wgroup_t 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;
@ -6068,6 +6102,26 @@ void InfoSync_Add(infosync_t *sync, void *context, const char *name)
sync->keys[k].syncpos = 0;
}
static qboolean InfoBuf_NeedsEncoding(const char *str, size_t size)
{
const char *c, *e = str+size;
for (c = str; c < e; c++)
{
switch((unsigned char)*c)
{
case 255: //invalid for vanilla qw, and also used for special encoding
case '\\': //abiguity with end-of-token
case '\"': //parsing often sends these enclosed in quotes
case '\n': //REALLY screws up parsing
case '\r': //generally bad form
case 0: //are we really doing this?
case '$': //a number of engines like expanding things inside quotes. make sure that cannot ever happen.
case ';': //in case someone manages to break out of quotes
return true;
}
}
return false;
}
qboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx)
{
size_t k;
@ -6106,14 +6160,18 @@ char *InfoBuf_ValueForKey (infobuf_t *info, const char *key) //not to be used wi
valueindex = (valueindex+1)&3;
return InfoBuf_ReadKey(info, key, value[valueindex], sizeof(value[valueindex]));
}
const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize) //obtains a direct pointer to temp memory
const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large) //obtains a direct pointer to temp memory
{
size_t k;
if (InfoBuf_FindKey(info, key, &k) && !info->keys[k].partial)
{
if (large)
*large = info->keys[k].large;
*blobsize = info->keys[k].size;
return info->keys[k].value;
}
if (large)
*large = InfoBuf_NeedsEncoding(key, sizeof(key));
*blobsize = 0;
return NULL;
}
@ -6169,26 +6227,25 @@ char *InfoBuf_DecodeString(const char *instart, const char *inend, size_t *sz)
}
return ret;
}
static qboolean InfoBuf_IsLarge(struct infokey_s *key)
{
size_t namesize;
if (key->partial)
return true;
//detect invalid keys/values
//\\ makes parsing really really messy and isn't supported by most clients (although we could do it anyway)
//\" requires string escapes, again compat issues.
//0xff bugs out vanilla.
//nulls are bad, too...
if (strchr(key->name, '\\') || strchr(key->name, '\"') || strchr(key->name, 0xff))
return true;
if (strchr(key->value, '\\') || strchr(key->value, '\"') || strchr(key->value, 0xff) || strlen(key->value) != key->size)
return true;
if (key->size >= 64)
return true; //key length limits is a thing in vanilla qw.
if (strlen(key->name) >= 64)
return true; //value length limits is a thing in vanilla qw.
//note that qw reads values up to 512, but only sets them up to 64 bytes...
//probably just so that people don't spot buffer overflows so easily.
namesize = strlen(key->name);
if (namesize >= 64)
return true; //key length limits is a thing in vanilla qw.
if (InfoBuf_NeedsEncoding(key->name, namesize))
return true;
if (InfoBuf_NeedsEncoding(key->value, key->size))
return true;
return false;
}
//like InfoBuf_SetStarBlobKey, but understands partials.
@ -6423,24 +6480,7 @@ static qboolean InfoBuf_EncodeString_Internal(const char *n, size_t s, char *out
{
size_t r = 0;
const char *c;
for (c = n; c < n+s; c++)
{
if (*c == (char)255 && c == n)
break;
if (*c == '\\') //abiguity with end-of-token
break;
if (*c == '\"') //parsing often sends these enclosed in quotes
break;
if (*c == '\n' || *c == '\r') //generally bad form
break;
if (*c == 0) //are we really doing this?
break;
if (*c == '$') //a number of engines like expanding things inside quotes. make sure that cannot ever happen.
break;
if (*c == ';') //in case someone manages to break out of quotes
break;
}
if (c != n+s)
if (InfoBuf_NeedsEncoding(n, s))
{
unsigned int base64_cur = 0;
unsigned int base64_bits = 0;

View File

@ -785,7 +785,7 @@ extern const char *basicuserinfos[]; //note: has a leading *
extern const char *privateuserinfos[]; //key names that are not broadcast from the server
qboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx);
const char *InfoBuf_KeyForNumber (infobuf_t *info, int num);
const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize);
const char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large);
char *InfoBuf_ReadKey (infobuf_t *info, const char *key, char *outbuf, size_t outsize);
char *InfoBuf_ValueForKey (infobuf_t *info, const char *key);
qboolean InfoBuf_RemoveKey (infobuf_t *info, const char *key);

View File

@ -314,6 +314,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define svcfte_updateentities 86
#define svcfte_brushedit 87 // networked brush editing, paired with clcfte_brushedit.
#define svcfte_updateseats 88 // byte count, byte playernum[count]
#define svcfte_setinfoblob 89 // [8] 1-based index [string] key [32] isfinal<<31|offset [16] chunksize [chunksize] data
//fitz svcs

View File

@ -212,7 +212,7 @@ void *Z_Malloc(int size)
}
#endif
void Z_StrCat(char **ptr, char *append)
void Z_StrCat(char **ptr, const char *append)
{
size_t oldlen = *ptr?strlen(*ptr):0;
size_t newlen = strlen(append);

View File

@ -133,7 +133,7 @@ void ZG_FreeGroup(zonegroup_t *ctx);
#endif
#define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s)
void Z_StrCat(char **ptr, char *append);
void Z_StrCat(char **ptr, const char *append);
/*
void *Hunk_Alloc (int size); // returns 0 filled memory

View File

@ -769,7 +769,7 @@ static void BE_ApplyAttributes(unsigned int bitstochange, unsigned int bitstoend
continue;
}
GL_SelectVBO(shaderstate.sourcevbo->bonenums.gl.vbo);
qglVertexAttribPointer(VATTR_BONENUMS, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr);
qglVertexAttribPointer(VATTR_BONENUMS, 4, GL_BONE_INDEX_TYPE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr);
break;
case VATTR_BONEWEIGHTS:
if (!shaderstate.sourcevbo->boneweights.gl.vbo && !shaderstate.sourcevbo->boneweights.gl.addr)
@ -1418,12 +1418,12 @@ static float *FTableForFunc ( unsigned int func )
}
}
void Shader_LightPass(const char *shortname, shader_t *s, const void *args)
void Shader_LightPass(struct shaderparsestate_s *ps, const char *shortname, const void *args)
{
char shadertext[8192*2];
extern cvar_t r_drawflat;
sprintf(shadertext, LIGHTPASS_SHADER, (r_lightmap.ival||r_drawflat.ival)?"#FLAT=1.0":"");
Shader_DefaultScript(shortname, s, shadertext);
Shader_DefaultScript(ps, shortname, shadertext);
}
void GenerateFogTexture(texid_t *tex, float density, float zscale)
@ -3404,6 +3404,9 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu
unsigned int ph;
const shaderprogparm_t *p;
if (perm->factorsuniform != -1)
qglUniform4fvARB(perm->factorsuniform, countof(shaderstate.curshader->factors), shaderstate.curshader->factors[0]);
/*don't bother setting it if the ent properties are unchanged (but do if the mesh changed)*/
if (entunchanged)
return;

View File

@ -54,7 +54,23 @@ typedef enum {
SHADER_SORT_COUNT
} shadersort_t;
#ifdef FTE_TARGET_WEB
#define MAX_BONES 256
#else
#define MAX_BONES 256 //Note: there's lots of bone data allocated on the stack, so don't bump recklessly.
#endif
#if MAX_BONES>65536
#define GL_BONE_INDEX_TYPE GL_UNSIGNED_INT
typedef unsigned int boneidx_t;
#elif MAX_BONES>256
#define GL_BONE_INDEX_TYPE GL_UNSIGNED_SHORT
typedef unsigned short boneidx_t;
#else
#define GL_BONE_INDEX_TYPE GL_UNSIGNED_BYTE
typedef unsigned char boneidx_t;
#endif
typedef boneidx_t bone_vec4_t[4];
struct doll_s;
void rag_uninstanciateall(void);
void rag_flushdolls(qboolean force);
@ -102,7 +118,7 @@ typedef struct mesh_s
qboolean istrifan; /*if its a fan/poly/single quad (permits optimisations)*/
const float *bones;
int numbones;
byte_vec4_t *bonenums;
bone_vec4_t *bonenums;
vec4_t *boneweights;
} mesh_t;
@ -1040,6 +1056,7 @@ typedef struct model_s
//
void *meshinfo; //data allocated within the memgroup allocations, will be nulled out when the model is flushed
zonegroup_t memgroup;
int camerabone; //the 1-based bone index that the camera should be attached to (for gltf rather than anything else)
} model_t;
#define MDLF_EMITREPLACE 0x0001 // particle effect engulphs model (don't draw)

File diff suppressed because it is too large Load Diff

View File

@ -1404,6 +1404,16 @@ static const char *glsl_hdrs[] =
// "#define s_deluxmap3 s_deluxemap3\n"
#endif
#endif
"#if defined(ORM) || defined(SG)\n"
"uniform vec4 factors[3];\n"
"#define factor_base factors[0]\n"
"#define factor_spec factors[1]\n"
"#define factor_emit factors[2]\n"
"#else\n"
"#define factor_base vec4(1.0)\n"
"#define factor_spec vec4(1.0)\n"
"#define factor_emit vec4(1.0)\n"
"#endif\n"
"#ifdef USEUBOS\n"
"layout(std140) uniform u_lightinfo\n"
"{"
@ -1819,6 +1829,67 @@ static const char *glsl_hdrs[] =
"#endif\n"
"}\n"
,
"sys/pbr.h",
//ripped from the gltf webgl demo.
//https://github.com/KhronosGroup/glTF-WebGL-PBR/blob/master/shaders/pbr-frag.glsl
//because most of this maths is gibberish, especially the odd magic number.
"#ifdef PBR\n"
"const float PI = 3.141592653589793;\n"
"vec3 diffuse(vec3 diffuseColor)\n" //Basic Lambertian diffuse
"{\n"
"return diffuseColor / PI;\n"
"}\n"
"vec3 specularReflection(vec3 reflectance0, vec3 reflectance90, float VdotH)\n"
"{\n"
"return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);\n"
"}\n"
"float geometricOcclusion(float NdotL, float NdotV, float r)\n"
"{\n"
"float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));\n"
"float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));\n"
"return attenuationL * attenuationV;\n"
"}\n"
"float microfacetDistribution(float alphaRoughness, float NdotH)\n" //Trowbridge-Reitz
"{\n"
"float roughnessSq = alphaRoughness * alphaRoughness;\n"
"float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;\n"
"return roughnessSq / (PI * f * f);\n"
"}\n"
"vec3 DoPBR(vec3 n, vec3 v, vec3 l, float perceptualRoughness, vec3 diffuseColor, vec3 specularColor, vec3 scales)\n"
"{\n"
// Compute reflectance.
"float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n"
"float alphaRoughness = perceptualRoughness * perceptualRoughness;\n"
// For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
// For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
"float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);\n"
"vec3 specularEnvironmentR0 = specularColor.rgb;\n"
"vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;\n"
"vec3 h = normalize(l+v);\n" // Half vector between both l and v
"vec3 reflection = -normalize(reflect(v, n));\n"
"float NdotL = clamp(dot(n, l), 0.001, 1.0);\n"
"float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);\n"
"float NdotH = clamp(dot(n, h), 0.0, 1.0);\n"
"float LdotH = clamp(dot(l, h), 0.0, 1.0);\n"
"float VdotH = clamp(dot(v, h), 0.0, 1.0);\n"
// Calculate the shading terms for the microfacet specular shading model
"vec3 F = specularReflection(specularEnvironmentR0, specularEnvironmentR90, VdotH);\n"
"float G = geometricOcclusion(NdotL, NdotV, alphaRoughness);\n"
"float D = microfacetDistribution(alphaRoughness, NdotH);\n"
// Calculation of analytical lighting contribution
"vec3 diffuseContrib = (1.0 - F) * diffuse(diffuseColor) * scales.y;\n"
"vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV) * scales.z;\n"
// Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
"return NdotL * (diffuseContrib + specContrib);\n"
"}\n"
"#endif\n"
,
"sys/pcf.h",
//!!cvardf r_glsl_pcf
"#ifndef PCF\n"
@ -2792,6 +2863,8 @@ static void GLSlang_ProgAutoFields(program_t *prog, struct programpermu_s *pp, c
pp->numparms = 0;
pp->parm = NULL;
pp->factorsuniform = qglGetUniformLocationARB(pp->h.glsl.handle, "factors");
for (i = 0; shader_unif_names[i].name; i++)
{
uniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, shader_unif_names[i].name);

View File

@ -2828,6 +2828,9 @@ static void GetEvent(void)
break;
case FocusIn:
//don't care about it if its just someone wiggling the mouse
if (event.xfocus.detail == NotifyPointer)
break;
//activeapp is if the game window is focused
vid.activeapp = true;
ClearAllStates(); //just in case.
@ -2848,6 +2851,9 @@ static void GetEvent(void)
// x11.pXUnmapWindow(vid_dpy, vid_decoywindow);
break;
case FocusOut:
//don't care about it if its just someone wiggling the mouse
if (event.xfocus.detail == NotifyPointer)
break;
//if we're already active, the decoy window shouldn't be focused anyway.
if (event.xfocus.window == vid_window)
x11.ime_shown = -1;

View File

@ -2958,10 +2958,16 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!cvardf r_tessellation_level=5\n"
"!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\n"
"!!samps =EIGHTBIT paletted 1\n"
//!!permu VC -- adds rgba vertex colour multipliers
//!!permu SPECULAR -- auto-added when gl_specular>0
//!!permu OFFSETMAPPING -- auto-added when r_glsl_offsetmapping is set
//!!permu NONORMALS -- states that there's no normals available, which affects lighting.
//!!permu VC // adds rgba vertex colour multipliers
//!!permu SPECULAR // auto-added when gl_specular>0
//!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set
//!!permu NONORMALS // states that there's no normals available, which affects lighting.
//!!permu ORM // specularmap is r:Occlusion, g:Roughness, b:Metalness
//!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent)
//!!permu PBR // an attempt at pbr logic (enabled from ORM or SG)
//!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid.
//!!permu EIGHTBIT // uses software-style paletted colourmap lookups
//!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level)
"#include \"sys/defs.h\"\n"
@ -2975,6 +2981,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#define affine\n"
"#endif\n"
"#if defined(ORM) || defined(SG)\n"
"#define PBR\n"
"#endif\n"
"#ifdef NONORMALS //lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture.\n"
"#undef BUMP\n"
@ -2986,7 +2995,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#ifdef VERTEX_SHADER\n"
"#include \"sys/skeletal.h\"\n"
@ -2995,7 +3003,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n"
"varying vec3 eyevector;\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"#if defined(PBR)||defined(REFLECTCUBEMASK)\n"
"varying mat3 invsurface;\n"
"#endif\n"
"#ifdef TESS\n"
@ -3015,22 +3023,28 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"vec3 n, s, t, w;\n"
"gl_Position = skeletaltransform_wnst(w,n,s,t);\n"
"n = normalize(n);\n"
"s = normalize(s);\n"
"t = normalize(t);\n"
"#ifndef PBR\n"
"float d = dot(n,e_light_dir);\n"
"if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex.\n"
"d = 0.0; //this avoids the dark side going below the ambient level.\n"
"light.rgb += (d*e_light_mul);\n"
"#else\n"
"light.rgb = vec3(1.0);\n"
"#endif\n"
"#endif\n"
"#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n"
"#if defined(PBR)\n"
"eyevector = e_eyepos - w.xyz;\n"
"#elif defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n"
"vec3 eyeminusvertex = e_eyepos - w.xyz;\n"
"eyevector.x = dot(eyeminusvertex, s.xyz);\n"
"eyevector.y = dot(eyeminusvertex, t.xyz);\n"
"eyevector.z = dot(eyeminusvertex, n.xyz);\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"invsurface[0] = s;\n"
"invsurface[1] = t;\n"
"invsurface[2] = n;\n"
"#if defined(PBR) || defined(REFLECTCUBEMASK)\n"
"invsurface = mat3(s, t, n);\n"
"#endif\n"
"tc = v_texcoord;\n"
@ -3183,10 +3197,39 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n"
"varying vec3 eyevector;\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"#if defined(PBR) || defined(REFLECTCUBEMASK)\n"
"varying mat3 invsurface;\n"
"#endif\n"
"#ifdef PBR\n"
"#include \"sys/pbr.h\"\n"
"#if 0\n"
"vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)\n"
"{\n"
"float mipCount = 9.0; // resolution of 512x512\n"
"float lod = (pbrInputs.perceptualRoughness * mipCount);\n"
// retrieve a scale and bias to F0. See [1], Figure 3
"vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;\n"
"vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;\n"
"#ifdef USE_TEX_LOD\n"
"vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;\n"
"#else\n"
"vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;\n"
"#endif\n"
"vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;\n"
"vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);\n"
// For presentation, this allows us to disable IBL terms
"diffuse *= u_ScaleIBLAmbient.x;\n"
"specular *= u_ScaleIBLAmbient.y;\n"
"return diffuse + specular;\n"
"}\n"
"#endif\n"
"#endif\n"
"void main ()\n"
"{\n"
@ -3218,33 +3261,87 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"col.rgb += lc.rgb*e_lowercolour*lc.a;\n"
"#endif\n"
"#if defined(BUMP) && defined(SPECULAR)\n"
"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\n"
"vec4 specs = texture2D(s_specular, tc);\n"
"col *= factor_base;\n"
"vec3 halfdir = normalize(normalize(eyevector) + e_light_dir);\n"
"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\n"
"col.rgb += FTE_SPECULAR_MULTIPLIER * spec * specs.rgb;\n"
"#elif defined(REFLECTCUBEMASK)\n"
"vec3 bumps = vec3(0, 0, 1);\n"
"#define dielectricSpecular 0.04\n"
"#ifdef SPECULAR\n"
"vec4 specs = texture2D(s_specular, tc)*factor_spec;\n"
"#ifdef ORM\n"
"#define occlusion specs.r\n"
"#define roughness clamp(specs.g, 0.04, 1.0)\n"
"#define metalness specs.b\n"
"#define gloss 1.0 //sqrt(1.0-roughness)\n"
"#define ambientrgb (specrgb+col.rgb)\n"
"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n"
"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n"
"#elif defined(SG) //pbr-style specular+glossiness\n"
//occlusion needs to be baked in. :(
"#define roughness (1.0-specs.a)\n"
"#define gloss (specs.a)\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb (specs.rgb+col.rgb)\n"
"#else //blinn-phong\n"
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb col.rgb\n"
"#endif\n"
"#else\n"
"#define roughness 0.3\n"
"#define specrgb 1.0 //vec3(dielectricSpecular)\n"
"#endif\n"
"#ifdef BUMP\n"
"#ifdef PBR //to modelspace\n"
"vec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0));\n"
"#else //stay in tangentspace\n"
"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\n"
"#endif\n"
"#else\n"
"#ifdef PBR //to modelspace\n"
"#define bumps normalize(invsurface[2])\n"
"#else //tangent space\n"
"#define bumps vec3(0.0, 0.0, 1.0)\n"
"#endif\n"
"#endif\n"
"#ifdef PBR\n"
//move everything to model space
"col.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb;\n"
"#elif defined(gloss)\n"
"vec3 halfdir = normalize(normalize(eyevector) - e_light_dir);\n"
"float specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss);\n"
"col.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb;\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"vec3 rtc = reflect(-eyevector, bumps);\n"
"#ifndef PBR\n"
"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\n"
"#endif\n"
"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\n"
"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n"
"#endif\n"
"#if defined(occlusion) && !defined(NOOCCLUDE)\n"
"col.rgb *= occlusion;\n"
"#endif\n"
"col *= light * e_colourident;\n"
"#ifdef FULLBRIGHT\n"
"vec4 fb = texture2D(s_fullbright, tc);\n"
// col.rgb = mix(col.rgb, fb.rgb, fb.a);
"col.rgb += fb.rgb * fb.a * e_glowmod.rgb;\n"
"col.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb;\n"
"#elif defined(PBR)\n"
"col.rgb += e_glowmod.rgb * factor_emit.rgb;\n"
"#endif\n"
"#endif\n"
"#ifdef ALPHATEST\n"
"if (!(col.a ALPHATEST))\n"
"discard;\n"
"#endif\n"
"gl_FragColor = fog4(col);\n"
"}\n"
"#endif\n"
@ -5508,6 +5605,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!samps lightmap deluxemap\n"
"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3\n"
"#if defined(ORM) || defined(SG)\n"
"#define PBR\n"
"#endif\n"
"#include \"sys/defs.h\"\n"
//this is what normally draws all of your walls, even with rtlights disabled
@ -5551,9 +5652,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\n"
"#endif\n"
"#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\n"
"invsurface[0] = v_svector;\n"
"invsurface[1] = v_tvector;\n"
"invsurface[2] = v_normal;\n"
"invsurface = mat3(v_svector, v_tvector, v_normal);\n"
"#endif\n"
"tc = v_texcoord;\n"
"#ifdef FLOW\n"
@ -5723,6 +5822,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#ifdef FRAGMENT_SHADER\n"
"#define s_colourmap s_t0\n"
"#include \"sys/pbr.h\"\n"
"#ifdef OFFSETMAPPING\n"
"#include \"sys/offsetmapping.h\"\n"
"#endif\n"
@ -5749,12 +5850,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#endif\n"
//yay, regular texture!
"gl_FragColor = texture2D(s_diffuse, tc);\n"
//Read the base texture (with EIGHTBIT only alpha is needed)
"vec4 col = texture2D(s_diffuse, tc);\n"
"#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\n"
"vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\n"
"#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\n"
"#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\n"
"vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.\n"
"#endif\n"
@ -5799,61 +5900,89 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#endif\n"
"#endif\n"
//add in specular, if applicable.
// col *= factor_base;
"#define dielectricSpecular 0.04\n"
"#ifdef SPECULAR\n"
"vec4 specs = texture2D(s_specular, tc);\n"
"vec4 specs = texture2D(s_specular, tc);//*factor_spec;\n"
"#ifdef ORM\n"
"#define occlusion specs.r\n"
"#define roughness specs.g\n"
"#define metalness specs.b\n"
"#define gloss (1.0-roughness)\n"
"#define ambientrgb (specrgb+col.rgb)\n"
"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n"
"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n"
"#elif defined(SG) //pbr-style specular+glossiness\n"
//occlusion needs to be baked in. :(
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb (specs.rgb+col.rgb)\n"
"#else //blinn-phong\n"
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb col.rgb\n"
"#endif\n"
"#else\n"
"#define roughness 0.3\n"
"#define specrgb 1.0 //vec3(dielectricSpecular)\n"
"#endif\n"
//add in specular, if applicable.
"#ifdef PBR\n"
"col.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb;\n"
"#elif defined(gloss)\n"
"vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead\n"
"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\n"
"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss);\n"
"spec *= FTE_SPECULAR_MULTIPLIER;\n"
//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.
//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,
//we default to something that is not garish when the light value is directly infront of every single pixel.
//we can justify this difference due to the rtlight editor etc showing the *4.
"gl_FragColor.rgb += spec * specs.rgb;\n"
"col.rgb += spec * specrgb;\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"vec3 rtc = reflect(normalize(-eyevector), norm);\n"
"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\n"
"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\n"
"gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n"
"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n"
"#endif\n"
"#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\n"
"lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\n"
"float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.\n"
"lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\n"
"gl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\n"
"gl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\n"
"gl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\n"
"col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\n"
"col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\n"
"col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\n"
"#else\n"
//now we have our diffuse+specular terms, modulate by lightmap values.
"gl_FragColor.rgb *= lightmaps.rgb;\n"
"col.rgb *= lightmaps.rgb;\n"
//add on the fullbright
"#ifdef FULLBRIGHT\n"
"gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb;\n"
"col.rgb += texture2D(s_fullbright, tc).rgb;\n"
"#endif\n"
"#endif\n"
//entity modifiers
"gl_FragColor = gl_FragColor * e_colourident;\n"
"col *= e_colourident;\n"
"#if defined(MASK)\n"
"#if defined(MASKLT)\n"
"if (gl_FragColor.a < MASK)\n"
"if (col.a < MASK)\n"
"discard;\n"
"#else\n"
"if (gl_FragColor.a >= MASK)\n"
"if (col.a >= MASK)\n"
"discard;\n"
"#endif\n"
"gl_FragColor.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\n"
"col.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\n"
"#endif\n"
//and finally hide it all if we're fogged.
"#ifdef FOG\n"
"gl_FragColor = fog4(gl_FragColor);\n"
"#endif\n"
"gl_FragColor = fog4(col);\n"
"}\n"
"#endif\n"
@ -10935,6 +11064,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!samps =PCF shadowmap\n"
"!!samps =CUBE projectionmap\n"
"#if defined(ORM) || defined(SG)\n"
"#define PBR\n"
"#endif\n"
"#include \"sys/defs.h\"\n"
//this is the main shader responsible for realtime dlights.
@ -10991,6 +11124,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"{\n"
"vec3 n, s, t, w;\n"
"gl_Position = skeletaltransform_wnst(w,n,s,t);\n"
"n = normalize(n);\n"
"s = normalize(s);\n"
"t = normalize(t);\n"
"tcbase = v_texcoord; //pass the texture coords straight through\n"
"#ifdef ORTHO\n"
"vec3 lightminusvertex = -l_lightdirection;\n"
@ -11019,9 +11155,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"eyevector.z = dot(eyeminusvertex, n.xyz);\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
"invsurface[0] = v_svector;\n"
"invsurface[1] = v_tvector;\n"
"invsurface[2] = v_normal;\n"
"invsurface = mat3(v_svector, v_tvector, v_normal);\n"
"#endif\n"
"#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\n"
//for texture projections/shadowmapping on dlights
@ -11151,6 +11285,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#include \"sys/offsetmapping.h\"\n"
"#endif\n"
"#include \"sys/pbr.h\"\n"
"void main ()\n"
"{\n"
"#ifdef ORTHO\n"
@ -11199,6 +11335,36 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"vec4 specs = texture2D(s_specular, tcbase);\n"
"#endif\n"
"#define dielectricSpecular 0.04\n"
"#ifdef SPECULAR\n"
"#ifdef ORM //pbr-style occlusion+roughness+metalness\n"
"#define occlusion specs.r\n"
"#define roughness clamp(specs.g, 0.04, 1.0)\n"
"#define metalness specs.b\n"
"#define gloss 1.0 //sqrt(1.0-roughness)\n"
"#define ambientrgb (specrgb+col.rgb)\n"
"vec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness);\n"
"bases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n"
"#elif defined(SG) //pbr-style specular+glossiness\n"
//occlusion needs to be baked in. :(
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb (specs.rgb+col.rgb)\n"
"#else //blinn-phong\n"
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb col.rgb\n"
"#endif\n"
"#else\n"
"#define roughness 0.3\n"
"#define specrgb 1.0 //vec3(dielectricSpecular)\n"
"#endif\n"
"#ifdef PBR\n"
"vec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale);\n"
"#else\n"
"vec3 diff;\n"
"#ifdef NOBUMP\n"
//surface can only support ambient lighting, even for lights that try to avoid it.
@ -11212,12 +11378,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\n"
"#endif\n"
"#endif\n"
"#ifdef SPECULAR\n"
"vec3 halfdir = normalize(normalize(eyevector) + nl);\n"
"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a)*float(SPECMUL);\n"
"diff += l_lightcolourscale.z * spec * specs.rgb;\n"
"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL);\n"
"diff += l_lightcolourscale.z * spec * specrgb;\n"
"#endif\n"
"#endif\n"
"#ifdef REFLECTCUBEMASK\n"
@ -11236,11 +11401,15 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
/*2d projection, not used*/
// diff *= texture2d(s_projectionmap, shadowcoord);
"#endif\n"
"#if defined(occlusion) && !defined(NOOCCLUDE)\n"
"diff *= occlusion;\n"
"#endif\n"
"#if defined(VERTEXCOLOURS)\n"
"diff *= vc.rgb * vc.a;\n"
"#endif\n"
"gl_FragColor = vec4(fog3additive(diff*colorscale*l_lightcolour), 1.0);\n"
"diff *= colorscale*l_lightcolour;\n"
"gl_FragColor = vec4(fog3additive(diff), 1.0);\n"
"}\n"
"#endif\n"

View File

@ -29,7 +29,8 @@ lights are then added over the top based upon the diffusemap, bumpmap and specul
#ifndef SHADER_H
#define SHADER_H
typedef void (shader_gen_t)(const char *name, shader_t*, const void *args);
struct shaderparsestate_s;
typedef void (shader_gen_t)(struct shaderparsestate_s *ps, const char *name, const void *args);
#define SHADER_TMU_MAX 16
#define SHADER_PASS_MAX 16
@ -512,6 +513,9 @@ struct programpermu_s
} hlsl;
#endif
} h;
#endif
#ifdef GLQUAKE
int factorsuniform;
#endif
unsigned int permutation;
unsigned int attrmask;
@ -675,6 +679,12 @@ struct shader_s
bucket_t bucket;
#define MATERIAL_FACTOR_BASE 0
#define MATERIAL_FACTOR_SPEC 1
#define MATERIAL_FACTOR_EMIT 2
#define MATERIAL_FACTOR_COUNT 3
vec4_t factors[MATERIAL_FACTOR_COUNT];
//arranged as a series of vec4s
/* struct
{
@ -711,15 +721,17 @@ cin_t *R_ShaderGetCinematic(shader_t *s);
cin_t *R_ShaderFindCinematic(const char *name);
shader_t *R_ShaderFind(const char *name); //does NOT increase the shader refcount.
void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultWaterShader(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args);
void Shader_DefaultSkin (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultSkinShell (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_Default2D (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultBSPLM (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultBSPQ1 (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultBSPQ2 (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultWaterShader (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultSkybox (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultCinematic (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_DefaultScript (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_PolygonShader (struct shaderparsestate_s *ps, const char *shortname, const void *args);
void Shader_ResetRemaps(void); //called on map changes to reset remapped shaders.
void Shader_DoReload(void); //called when the shader system dies.

View File

@ -381,7 +381,7 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals)
return;
}
progfuncs->funcs.debug_trace = -10;
progfuncs->funcs.debug_trace = -10; //PR_StringToNative(+via PR_ValueString) has various error conditions that we want to mute instead of causing recursive errors.
//point this to the function's locals
globalbase = (int *)pr_globals + pr_xfunction->parm_start + pr_xfunction->locals;
@ -413,16 +413,16 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals)
{
progs = prnum;
externs->Printf ("<%s>\n", pr_progstate[progs].filename);
externs->DPrintf ("<%s>\n", pr_progstate[progs].filename);
}
if (!f->s_file)
externs->Printf ("stripped : %s\n", PR_StringToNative(ppf, f->s_name));
externs->Printf ("unknown-file : %s\n", PR_StringToNative(ppf, f->s_name));
else
{
if (pr_progstate[progs].linenums)
externs->Printf ("%12s:%i: %s\n", PR_StringToNative(ppf, f->s_file), pr_progstate[progs].linenums[st], PR_StringToNative(ppf, f->s_name));
else
externs->Printf ("%12s : %s\n", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name));
externs->Printf ("%12s : %s+%i\n", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name), st-f->first_statement);
}
//locals:0 = no locals
@ -460,7 +460,7 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals)
}
else
{
externs->Printf(" %s: %s\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false));
externs->Printf(" %s: %s\n", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false));
if (local->type == ev_vector)
ofs+=2;
}

View File

@ -6262,7 +6262,7 @@ static void QCBUILTIN PF_sv_serverkeyblob (pubprogfuncs_t *prinst, struct global
{
size_t blobsize = 0;
const char *key = PR_GetStringOfs(prinst, OFS_PARM0);
const char *blobvalue = InfoBuf_BlobForKey(&svs.info, key, &blobsize);
const char *blobvalue = InfoBuf_BlobForKey(&svs.info, key, &blobsize, NULL);
if ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0)
G_INT(OFS_RETURN) = blobsize; //no pointer to write to, just return the length.
@ -6302,7 +6302,7 @@ static void QCBUILTIN PF_getlocalinfo (pubprogfuncs_t *prinst, struct globalvars
{
size_t blobsize = 0;
const char *key = PR_GetStringOfs(prinst, OFS_PARM0);
const char *blobvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize);
const char *blobvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize, NULL);
if ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0)
G_INT(OFS_RETURN) = blobsize; //no pointer to write to, just return the length.
@ -10565,7 +10565,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
#ifndef QUAKETC
//mvdsv (don't require ebfs usage in qw)
{"executecommand", PF_ExecuteCommand, 0, 0, 0, 83, D("void()","Attempt to flush the localcmd buffer NOW. This is unsafe, as many events might cause the map to be purged while still executing qc code."), true},
{"mvdtokenize", PF_Tokenize, 0, 0, 0, 84, D("void(string str)",NULL), true},
{"mvdtokenize", PF_tokenize_console,0, 0, 0, 84, D("void(string str)",NULL), true},
{"mvdargc", PF_ArgC, 0, 0, 0, 85, D("float()",NULL), true},
{"mvdargv", PF_ArgV, 0, 0, 0, 86, D("string(float num)",NULL), true},

View File

@ -562,6 +562,9 @@ typedef struct client_s
#define SENDFLAGS_PRESENT 0x80000000u //this entity is present on that client
#define SENDFLAGS_REMOVED 0x40000000u //to handle remove packetloss
#ifndef NOLEGACY
char *dlqueue; //name\name delimited list of files to ask the client to download.
#endif
char downloadfn[MAX_QPATH];
vfsfile_t *download; // file being downloaded
qofs_t downloadsize; // total bytes
@ -1313,6 +1316,10 @@ void SV_UpdateToReliableMessages (void);
void SV_FlushBroadcasts (void);
qboolean SV_CanTrack(client_t *client, int entity);
#ifndef NOLEGACY
void SV_DownloadQueueNext(client_t *client);
void SV_DownloadQueueClear(client_t *client);
#endif
#ifdef NQPROT
void SV_DarkPlacesDownloadChunk(client_t *cl, sizebuf_t *msg);
#endif

View File

@ -117,8 +117,8 @@ extern cvar_t net_enable_dtls;
cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.");
cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.");
cvar_t sv_highchars = CVAR("sv_highchars", "1");
cvar_t sv_maxrate = CVARCD("sv_maxrate", "50k", CvarPostfixKMG, "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.");
cvar_t sv_maxdrate = CVARAFCD("sv_maxdrate", "500k",
cvar_t sv_maxrate = CVARCD("sv_maxrate", "50000", CvarPostfixKMG, "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.");
cvar_t sv_maxdrate = CVARAFCD("sv_maxdrate", "500000",
"sv_maxdownloadrate", 0, CvarPostfixKMG, "This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)");
cvar_t sv_minping = CVARFD("sv_minping", "", CVAR_SERVERINFO, "Simulate fake lag for any players with a ping under the value specified here. Value is in milliseconds.");
@ -619,6 +619,9 @@ void SV_DropClient (client_t *drop)
Con_TPrintf ("Client \"%s\" removed\n",drop->name);
}
#ifndef NOLEGACY
SV_DownloadQueueClear(drop);
#endif
if (drop->download)
{
VFS_CLOSE (drop->download);
@ -5456,7 +5459,7 @@ into a more C freindly form.
*/
void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
{
char *val, *p;
const char *val, *p;
int i;
client_t *client;
int dupc = 1;
@ -5464,6 +5467,8 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
#ifdef SVRANKING
extern cvar_t rank_filename;
#endif
size_t blobsize;
qboolean large;
int bottom = atoi(InfoBuf_ValueForKey(&cl->userinfo, "bottomcolor"));
@ -5481,16 +5486,19 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
Q_strncpyz (cl->team, val, sizeof(cl->teambuf));
// name for C code
val = InfoBuf_ValueForKey (&cl->userinfo, "name");
if (cl->protocol != SCP_BAD || *val)
val = InfoBuf_BlobForKey (&cl->userinfo, "name", &blobsize, &large);
if (!val)
val = "";
//we block large names here because a) they're unwieldly. b) they might cause players to be invisible to older clients/server browsers/etc.
//bots with no name skip the fixup, to avoid default names(they're expected to be given a name eventually, so are allowed to be invisible for now)
if (large || (cl->protocol == SCP_BAD && !*val))
newname[0] = 0;
else
{
SV_FixupName(val, newname, sizeof(newname));
if (strlen(newname) > 40)
newname[40] = 0;
}
else
newname[0] = 0;
deleetstring(basic, newname);
if (cl->protocol != SCP_BAD)

View File

@ -2786,14 +2786,13 @@ static qboolean SV_SyncInfoBuf(client_t *client)
infobuf_t *info = client->infosync.keys[0].context;
size_t bloboffset = client->infosync.keys[0].syncpos;
//unsigned int seat = info - cls.userinfo;
qboolean large;
size_t blobsize;
const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize);
const char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, &large);
size_t sendsize;
size_t bufferspace;
qboolean final;
char enckey[2048];
char encval[2048];
if (client->protocol == SCP_QUAKE2)
{ //q2 gamecode is fully responsible for networking this via configstrings.
@ -2806,36 +2805,17 @@ static qboolean SV_SyncInfoBuf(client_t *client)
if (client->netchan.message.cursize >= MAX_BACKBUFLEN/2)
return false; //don't bother trying to send anything.
if (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey)))
{
InfoSync_Remove(&client->infosync, 0);
return false;
}
sendsize = blobsize - bloboffset;
bufferspace = MAX_BACKBUFLEN - client->netchan.message.cursize;
bufferspace -= 7 - strlen(enckey) - 2; //extra overhead
bufferspace = (bufferspace/4)*3; //encoding overhead
sendsize = min(bufferspace, sendsize);
final = (bloboffset+sendsize >= blobsize);
if (!InfoBuf_EncodeString(blobdata+bloboffset, sendsize, encval, sizeof(encval)))
{
InfoSync_Remove(&client->infosync, 0);
return false;
}
if (final && !bloboffset && *enckey != '\xff' && *encval != '\xff')
if (!large)
{ //vanilla-compatible info.
if (ISNQCLIENT(client))
{ //except that nq never had any userinfo
const char *s;
if (info == &svs.info)
s = va("//svi \"%s\" \"%s\"\n", enckey, encval);
s = va("//svi \"%s\" \"%s\"\n", key, blobdata);
else
{
int playerslot = (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients;
s = va("//ui %i \"%s\" \"%s\"\n", playerslot, enckey, encval);
s = va("//ui %i \"%s\" \"%s\"\n", playerslot, key, blobdata);
}
ClientReliableWrite_Begin(client, svc_stufftext, strlen(s)+2);
ClientReliableWrite_String(client, s);
@ -2843,41 +2823,53 @@ static qboolean SV_SyncInfoBuf(client_t *client)
else
{
if (info == &svs.info)
ClientReliableWrite_Begin(client, svc_serverinfo, 1+strlen(enckey)+1+strlen(encval)+1);
ClientReliableWrite_Begin(client, svc_serverinfo, 1+strlen(key)+1+strlen(blobdata)+1);
else
{
ClientReliableWrite_Begin(client, svc_setinfo, 2+strlen(enckey)+1+strlen(encval)+1);
ClientReliableWrite_Begin(client, svc_setinfo, 2+strlen(key)+1+strlen(blobdata)+1);
ClientReliableWrite_Byte(client, (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients);
}
ClientReliableWrite_String(client, enckey);
ClientReliableWrite_String(client, encval);
ClientReliableWrite_String(client, key);
ClientReliableWrite_String(client, blobdata);
}
}
else if (client->fteprotocolextensions2 & PEXT2_INFOBLOBS)
{
int pl;
char enckey[2048];
unsigned int pl;
if (info == &svs.info)
pl = 255; //colourmaps being 1-based with these being 0-based means that only 0-254 are valid players, and 255 is unused, so lets use it for serverinfo blobs.
pl = 0; //colourmaps being 1-based with these being 0-based means that only 0-254 are valid players, and 255 is unused, so lets use it for serverinfo blobs.
else
pl = (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients;
pl = 1+((client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients);
ClientReliableWrite_Begin(client, svc_setinfo, 7+strlen(enckey)+1+strlen(encval)+1);
ClientReliableWrite_Byte(client, 255); //special meaning to say that this is a partial update
ClientReliableWrite_Byte(client, pl);
ClientReliableWrite_Long(client, (final?0x80000000:0)|bloboffset);
if (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey)))
{
InfoSync_Remove(&client->infosync, 0);
return false;
}
sendsize = blobsize - bloboffset;
bufferspace = MAX_BACKBUFLEN - client->netchan.message.cursize;
bufferspace -= 8 - strlen(enckey) - 1; //extra overhead
sendsize = min(bufferspace, sendsize);
final = (bloboffset+sendsize >= blobsize);
ClientReliableWrite_Begin(client, svcfte_setinfoblob, 8+strlen(enckey)+1+sendsize);
ClientReliableWrite_Byte(client, pl); //special meaning to say that this is a partial update
ClientReliableWrite_String(client, enckey);
ClientReliableWrite_String(client, encval);
}
else
{ //client can't receive this info, stop trying to send it.
InfoSync_Remove(&client->infosync, 0);
return true;
}
ClientReliableWrite_Long(client, (final?0x80000000:0)|bloboffset);
ClientReliableWrite_Short(client, sendsize);
ClientReliableWrite_SZ(client, blobdata+bloboffset, sendsize);
if (bloboffset+sendsize == blobsize)
InfoSync_Remove(&client->infosync, 0);
else
client->infosync.keys[0].syncpos += sendsize;
if (!final)
{
client->infosync.keys[0].syncpos += sendsize;
return true;
}
}
//else client can't receive this info, stop trying to send it.
InfoSync_Remove(&client->infosync, 0);
return true;
}

View File

@ -2472,6 +2472,10 @@ static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezf
host_client->downloadstarted = false;
#ifndef NOLEGACY
SV_DownloadQueueNext(host_client);
#endif
}
}
@ -2551,6 +2555,9 @@ void SV_NextDownload_f (void)
VFS_CLOSE (host_client->download);
host_client->download = NULL;
#ifndef NOLEGACY
SV_DownloadQueueNext(host_client);
#endif
}
void VARGS OutofBandPrintf(netadr_t *where, char *fmt, ...)
@ -2996,6 +3003,8 @@ qboolean SV_AllowDownload (const char *name)
return false;
if (*name == '/') //no absolute.
return false;
if (strchr(name, ':')) //no drives, alternative resources, etc. the filesystem should refuse such a file so this should just be paranoia.
return false;
if (strchr(name, '\\')) //no windows paths - grow up you lame windows users.
return false;
@ -3037,7 +3046,7 @@ qboolean SV_AllowDownload (const char *name)
if (Q_strncasecmp(name, "sound/", 6) == 0)
return !!allow_download_sounds.value;
//particles
if (Q_strncasecmp(name, "particles/", 6) == 0)
if (Q_strncasecmp(name, "particles/", 10) == 0)
return !!allow_download_particles.value;
//demos
if (Q_strncasecmp(name, "demos/", 6) == 0)
@ -3290,6 +3299,50 @@ void SV_DownloadSize_f(void)
}
#ifdef MVD_RECORDING
#ifndef NOLEGACY
void SV_DownloadQueueAdd(client_t *client, const char *name)
{
if (!client->dlqueue)
{
client->dlqueue = Z_StrDup(name);
SV_ClientPrintf (client, PRINT_HIGH, "Using legacy serverside download queue. This is subject to race conditions, be careful.\n");
}
else
{
Z_StrCat(&client->dlqueue, "\\");
Z_StrCat(&client->dlqueue, name);
}
}
void SV_DownloadQueueNext(client_t *client)
{
char buf[MAX_QPATH*2];
char *name = client->dlqueue;
char *next;
if (!name)
return;
next = strchr(name, '\\');
if (next)
{
host_client->dlqueue = Z_StrDup(next+1);
*next = 0;
}
else
client->dlqueue = NULL;
next = va("download \"%s\"\n", COM_QuotedString(name, buf, sizeof(buf), true));
ClientReliableWrite_Begin (client, svc_stufftext, 2+strlen(next));
ClientReliableWrite_String (client, next);
Z_Free(name);
}
void SV_DownloadQueueClear(client_t *client)
{
if (client->dlqueue)
Z_Free(client->dlqueue);
client->dlqueue = NULL;
}
#endif
void SV_DemoDownload_f(void)
{
int arg;
@ -3306,7 +3359,16 @@ void SV_DemoDownload_f(void)
name = Cmd_Argv(1);
if (!strcmp(name, "\\") || !Q_strcasecmp(name, "stop") || !Q_strcasecmp(name, "cancel"))
{
//fte servers don't do download queues, as it is impossible to avoid race conditions with vanilla clients anyway.
if (strcmp(name, "\\"))
{ //cancel/stop kill any current download too. which is annoying
if (host_client->download)
VFS_CLOSE (host_client->download);
host_client->download = NULL;
host_client->downloadstarted = false;
}
#ifndef NOLEGACY
SV_DownloadQueueClear(host_client);
#endif
return;
}
}
@ -3339,6 +3401,14 @@ void SV_DemoDownload_f(void)
if (!mvdname)
SV_ClientPrintf (host_client, PRINT_HIGH, "%s is an invalid MVD demonum.\n", name);
#ifndef NOLEGACY
else if (!(host_client->protocol & PEXT_CHUNKEDDOWNLOADS) || !strncmp(InfoBuf_ValueForKey(&host_client->userinfo, "*client"), "ezQuake", 7))
{ //chunked downloads was built around the client being in control (because only it knows which files are needed)
//but ezquake never implemented that part
SV_DownloadQueueAdd(host_client, va("demos/%s", mvdname));
continue;
}
#endif
else
{
const char *s = va("download \"demos/%s\"\n", mvdname);
@ -3346,6 +3416,10 @@ void SV_DemoDownload_f(void)
ClientReliableWrite_String (host_client, s);
}
}
#ifndef NOLEGACY
if (!host_client->download)
SV_DownloadQueueNext(host_client);
#endif
}
#endif
@ -3458,7 +3532,7 @@ void SV_BeginDownload_f(void)
}
}
if (!host_client->download)
if (!host_client->download && !result)
result = -1; //this isn't likely, but hey.
//handle errors
@ -3514,6 +3588,7 @@ void SV_BeginDownload_f(void)
}
if (ISNQCLIENT(host_client))
host_client->send_message = true;
SV_DownloadQueueNext(host_client);
return;
}
@ -3588,6 +3663,11 @@ void SV_StopDownload_f(void)
SV_ClientPrintf(host_client, PRINT_HIGH, "Can't stop download - not downloading anything\n");
host_client->downloadstarted = false;
#ifndef NOLEGACY
SV_DownloadQueueNext(host_client);
// SV_DownloadQueueClear(host_client);
#endif
}
//=============================================================================

View File

@ -18,8 +18,11 @@
//!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set
//!!permu NONORMALS // states that there's no normals available, which affects lighting.
//!!permu ORM // specularmap is r:Occlusion, g:Roughness, b:Metalness
//!!permu F0R // specularmap is rgb:F0, a:Roughness (instead of exponent)
//!!permu PBR // an attempt at pbr logic
//!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent)
//!!permu PBR // an attempt at pbr logic (enabled from ORM or SG)
//!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid.
//!!permu EIGHTBIT // uses software-style paletted colourmap lookups
//!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level)
#include "sys/defs.h"
@ -33,6 +36,9 @@
#define affine
#endif
#if defined(ORM) || defined(SG)
#define PBR
#endif
#ifdef NONORMALS //lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture.
#undef BUMP
@ -44,7 +50,6 @@
#ifdef VERTEX_SHADER
#include "sys/skeletal.h"
@ -53,7 +58,7 @@ varying vec4 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
varying vec3 eyevector;
#endif
#ifdef REFLECTCUBEMASK
#if defined(PBR)||defined(REFLECTCUBEMASK)
varying mat3 invsurface;
#endif
#ifdef TESS
@ -73,22 +78,28 @@ void main ()
vec3 n, s, t, w;
gl_Position = skeletaltransform_wnst(w,n,s,t);
n = normalize(n);
float d = dot(n,e_light_dir);
if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex.
d = 0.0; //this avoids the dark side going below the ambient level.
light.rgb += (d*e_light_mul);
s = normalize(s);
t = normalize(t);
#ifndef PBR
float d = dot(n,e_light_dir);
if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex.
d = 0.0; //this avoids the dark side going below the ambient level.
light.rgb += (d*e_light_mul);
#else
light.rgb = vec3(1.0);
#endif
#endif
#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
#if defined(PBR)
eyevector = e_eyepos - w.xyz;
#elif defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
vec3 eyeminusvertex = e_eyepos - w.xyz;
eyevector.x = dot(eyeminusvertex, s.xyz);
eyevector.y = dot(eyeminusvertex, t.xyz);
eyevector.z = dot(eyeminusvertex, n.xyz);
#endif
#ifdef REFLECTCUBEMASK
invsurface[0] = s;
invsurface[1] = t;
invsurface[2] = n;
#if defined(PBR) || defined(REFLECTCUBEMASK)
invsurface = mat3(s, t, n);
#endif
tc = v_texcoord;
@ -241,10 +252,39 @@ varying vec4 light;
#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)
varying vec3 eyevector;
#endif
#ifdef REFLECTCUBEMASK
#if defined(PBR) || defined(REFLECTCUBEMASK)
varying mat3 invsurface;
#endif
#ifdef PBR
#include "sys/pbr.h"
#if 0
vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
{
float mipCount = 9.0; // resolution of 512x512
float lod = (pbrInputs.perceptualRoughness * mipCount);
// retrieve a scale and bias to F0. See [1], Figure 3
vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;
vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;
#ifdef USE_TEX_LOD
vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;
#else
vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;
#endif
vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
// For presentation, this allows us to disable IBL terms
diffuse *= u_ScaleIBLAmbient.x;
specular *= u_ScaleIBLAmbient.y;
return diffuse + specular;
}
#endif
#endif
void main ()
{
@ -276,33 +316,87 @@ void main ()
col.rgb += lc.rgb*e_lowercolour*lc.a;
#endif
#if defined(BUMP) && defined(SPECULAR)
vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);
vec4 specs = texture2D(s_specular, tc);
col *= factor_base;
vec3 halfdir = normalize(normalize(eyevector) + e_light_dir);
float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a);
col.rgb += FTE_SPECULAR_MULTIPLIER * spec * specs.rgb;
#elif defined(REFLECTCUBEMASK)
vec3 bumps = vec3(0, 0, 1);
#define dielectricSpecular 0.04
#ifdef SPECULAR
vec4 specs = texture2D(s_specular, tc)*factor_spec;
#ifdef ORM
#define occlusion specs.r
#define roughness clamp(specs.g, 0.04, 1.0)
#define metalness specs.b
#define gloss 1.0 //sqrt(1.0-roughness)
#define ambientrgb (specrgb+col.rgb)
vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);
col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);
#elif defined(SG) //pbr-style specular+glossiness
//occlusion needs to be baked in. :(
#define roughness (1.0-specs.a)
#define gloss (specs.a)
#define specrgb specs.rgb
#define ambientrgb (specs.rgb+col.rgb)
#else //blinn-phong
#define roughness (1.0-specs.a)
#define gloss specs.a
#define specrgb specs.rgb
#define ambientrgb col.rgb
#endif
#else
#define roughness 0.3
#define specrgb 1.0 //vec3(dielectricSpecular)
#endif
#ifdef BUMP
#ifdef PBR //to modelspace
vec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0));
#else //stay in tangentspace
vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);
#endif
#else
#ifdef PBR //to modelspace
#define bumps normalize(invsurface[2])
#else //tangent space
#define bumps vec3(0.0, 0.0, 1.0)
#endif
#endif
#ifdef PBR
//move everything to model space
col.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb;
#elif defined(gloss)
vec3 halfdir = normalize(normalize(eyevector) - e_light_dir);
float specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss);
col.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb;
#endif
#ifdef REFLECTCUBEMASK
vec3 rtc = reflect(-eyevector, bumps);
rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];
#ifndef PBR
rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];
#endif
rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;
col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;
#endif
#if defined(occlusion) && !defined(NOOCCLUDE)
col.rgb *= occlusion;
#endif
col *= light * e_colourident;
#ifdef FULLBRIGHT
vec4 fb = texture2D(s_fullbright, tc);
// col.rgb = mix(col.rgb, fb.rgb, fb.a);
col.rgb += fb.rgb * fb.a * e_glowmod.rgb;
col.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb;
#elif defined(PBR)
col.rgb += e_glowmod.rgb * factor_emit.rgb;
#endif
#endif
#ifdef ALPHATEST
if (!(col.a ALPHATEST))
discard;
#endif
gl_FragColor = fog4(col);
}
#endif

View File

@ -15,6 +15,10 @@
!!samps lightmap deluxemap
!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3
#if defined(ORM) || defined(SG)
#define PBR
#endif
#include "sys/defs.h"
//this is what normally draws all of your walls, even with rtlights disabled
@ -58,9 +62,7 @@ void main ()
eyevector.z = dot(eyeminusvertex, v_normal.xyz);
#endif
#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)
invsurface[0] = v_svector;
invsurface[1] = v_tvector;
invsurface[2] = v_normal;
invsurface = mat3(v_svector, v_tvector, v_normal);
#endif
tc = v_texcoord;
#ifdef FLOW
@ -230,6 +232,8 @@ void main()
#ifdef FRAGMENT_SHADER
#define s_colourmap s_t0
#include "sys/pbr.h"
#ifdef OFFSETMAPPING
#include "sys/offsetmapping.h"
#endif
@ -256,12 +260,12 @@ void main ()
#endif
//yay, regular texture!
gl_FragColor = texture2D(s_diffuse, tc);
//Read the base texture (with EIGHTBIT only alpha is needed)
vec4 col = texture2D(s_diffuse, tc);
#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))
vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);
#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)
#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)
vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.
#endif
@ -306,61 +310,89 @@ void main ()
#endif
#endif
//add in specular, if applicable.
#ifdef SPECULAR
vec4 specs = texture2D(s_specular, tc);
vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead
float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);
spec *= FTE_SPECULAR_MULTIPLIER;
//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.
//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,
//we default to something that is not garish when the light value is directly infront of every single pixel.
//we can justify this difference due to the rtlight editor etc showing the *4.
gl_FragColor.rgb += spec * specs.rgb;
#endif
// col *= factor_base;
#define dielectricSpecular 0.04
#ifdef SPECULAR
vec4 specs = texture2D(s_specular, tc);//*factor_spec;
#ifdef ORM
#define occlusion specs.r
#define roughness specs.g
#define metalness specs.b
#define gloss (1.0-roughness)
#define ambientrgb (specrgb+col.rgb)
vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);
col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);
#elif defined(SG) //pbr-style specular+glossiness
//occlusion needs to be baked in. :(
#define roughness (1.0-specs.a)
#define gloss specs.a
#define specrgb specs.rgb
#define ambientrgb (specs.rgb+col.rgb)
#else //blinn-phong
#define roughness (1.0-specs.a)
#define gloss specs.a
#define specrgb specs.rgb
#define ambientrgb col.rgb
#endif
#else
#define roughness 0.3
#define specrgb 1.0 //vec3(dielectricSpecular)
#endif
//add in specular, if applicable.
#ifdef PBR
col.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb;
#elif defined(gloss)
vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead
float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss);
spec *= FTE_SPECULAR_MULTIPLIER;
//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.
//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,
//we default to something that is not garish when the light value is directly infront of every single pixel.
//we can justify this difference due to the rtlight editor etc showing the *4.
col.rgb += spec * specrgb;
#endif
#ifdef REFLECTCUBEMASK
vec3 rtc = reflect(normalize(-eyevector), norm);
rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];
rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;
gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;
col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;
#endif
#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.
lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.
float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.
lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest
gl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.
gl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.
gl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.
col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.
col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.
col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.
#else
//now we have our diffuse+specular terms, modulate by lightmap values.
gl_FragColor.rgb *= lightmaps.rgb;
col.rgb *= lightmaps.rgb;
//add on the fullbright
#ifdef FULLBRIGHT
gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb;
col.rgb += texture2D(s_fullbright, tc).rgb;
#endif
#endif
//entity modifiers
gl_FragColor = gl_FragColor * e_colourident;
col *= e_colourident;
#if defined(MASK)
#if defined(MASKLT)
if (gl_FragColor.a < MASK)
if (col.a < MASK)
discard;
#else
if (gl_FragColor.a >= MASK)
if (col.a >= MASK)
discard;
#endif
gl_FragColor.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.
col.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.
#endif
//and finally hide it all if we're fogged.
#ifdef FOG
gl_FragColor = fog4(gl_FragColor);
#endif
gl_FragColor = fog4(col);
}
#endif

View File

@ -13,6 +13,10 @@
!!samps =PCF shadowmap
!!samps =CUBE projectionmap
#if defined(ORM) || defined(SG)
#define PBR
#endif
#include "sys/defs.h"
//this is the main shader responsible for realtime dlights.
@ -69,6 +73,9 @@ void main ()
{
vec3 n, s, t, w;
gl_Position = skeletaltransform_wnst(w,n,s,t);
n = normalize(n);
s = normalize(s);
t = normalize(t);
tcbase = v_texcoord; //pass the texture coords straight through
#ifdef ORTHO
vec3 lightminusvertex = -l_lightdirection;
@ -97,9 +104,7 @@ void main ()
eyevector.z = dot(eyeminusvertex, n.xyz);
#endif
#ifdef REFLECTCUBEMASK
invsurface[0] = v_svector;
invsurface[1] = v_tvector;
invsurface[2] = v_normal;
invsurface = mat3(v_svector, v_tvector, v_normal);
#endif
#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)
//for texture projections/shadowmapping on dlights
@ -229,6 +234,8 @@ void main()
#include "sys/offsetmapping.h"
#endif
#include "sys/pbr.h"
void main ()
{
#ifdef ORTHO
@ -277,25 +284,54 @@ void main ()
vec4 specs = texture2D(s_specular, tcbase);
#endif
vec3 diff;
#ifdef NOBUMP
//surface can only support ambient lighting, even for lights that try to avoid it.
diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);
#else
vec3 nl = normalize(lightvector);
#ifdef BUMP
diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));
#define dielectricSpecular 0.04
#ifdef SPECULAR
#ifdef ORM //pbr-style occlusion+roughness+metalness
#define occlusion specs.r
#define roughness clamp(specs.g, 0.04, 1.0)
#define metalness specs.b
#define gloss 1.0 //sqrt(1.0-roughness)
#define ambientrgb (specrgb+col.rgb)
vec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness);
bases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);
#elif defined(SG) //pbr-style specular+glossiness
//occlusion needs to be baked in. :(
#define roughness (1.0-specs.a)
#define gloss specs.a
#define specrgb specs.rgb
#define ambientrgb (specs.rgb+col.rgb)
#else //blinn-phong
#define roughness (1.0-specs.a)
#define gloss specs.a
#define specrgb specs.rgb
#define ambientrgb col.rgb
#endif
#else
//we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too.
diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
#define roughness 0.3
#define specrgb 1.0 //vec3(dielectricSpecular)
#endif
#endif
#ifdef SPECULAR
vec3 halfdir = normalize(normalize(eyevector) + nl);
float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * specs.a)*float(SPECMUL);
diff += l_lightcolourscale.z * spec * specs.rgb;
#ifdef PBR
vec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale);
#else
vec3 diff;
#ifdef NOBUMP
//surface can only support ambient lighting, even for lights that try to avoid it.
diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);
#else
vec3 nl = normalize(lightvector);
#ifdef BUMP
diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));
#else
//we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too.
diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
#endif
#endif
#ifdef SPECULAR
vec3 halfdir = normalize(normalize(eyevector) + nl);
float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL);
diff += l_lightcolourscale.z * spec * specrgb;
#endif
#endif
#ifdef REFLECTCUBEMASK
@ -314,11 +350,15 @@ void main ()
/*2d projection, not used*/
// diff *= texture2d(s_projectionmap, shadowcoord);
#endif
#if defined(occlusion) && !defined(NOOCCLUDE)
diff *= occlusion;
#endif
#if defined(VERTEXCOLOURS)
diff *= vc.rgb * vc.a;
#endif
gl_FragColor = vec4(fog3additive(diff*colorscale*l_lightcolour), 1.0);
diff *= colorscale*l_lightcolour;
gl_FragColor = vec4(fog3additive(diff), 1.0);
}
#endif

View File

@ -4668,7 +4668,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre
}
#endif
}
if (info->srgb != 1 && (vid.flags & VID_SRGB_FB))
if (info->srgb > 0 && (vid.flags & VID_SRGB_FB))
vid.flags |= VID_SRGBAWARE;
return true;
}

View File

@ -8,6 +8,12 @@ extern modplugfuncs_t *modfuncs;
#define GLTFMODELS
//'The units for all linear distances are meters.'
//'feh: 1 metre is approx. 26.24671916 qu.'
//if the player is 1.6m tall, and the player's model is around 48qu, then 1m=30qu, which is a slightly nicer number to work with, and 1qu is a really poorly defined unit.
#define GLTFSCALE 30
#ifdef GLTFMODELS
typedef struct json_s
{
@ -283,6 +289,10 @@ static qintptr_t JSON_GetInteger(json_t *t, const char *child, int fallback)
l = MAX_QPATH-1;
memcpy(tmp, t->bodystart, l);
tmp[l] = 0;
if (!strcmp(tmp, "false")) //special cases, for booleans
return 0;
if (!strcmp(tmp, "true")) //special cases, for booleans
return 1;
return (qintptr_t)strtoll(tmp, NULL, 0);
}
return fallback;
@ -490,11 +500,12 @@ typedef struct gltf_s
unsigned int numsurfaces;
json_t *r;
int bonemap[MAX_BONES]; //remap skinned bones. I hate that we have to do this.
int *bonemap;//[MAX_BONES]; //remap skinned bones. I hate that we have to do this.
struct gltfbone_s
{
char name[32];
int parent;
int camera;
double amatrix[16];
double inverse[16];
struct
@ -507,7 +518,7 @@ typedef struct gltf_s
struct gltf_accessor *input;
struct gltf_accessor *output;
} *rot, *scale, *translation;
} bones[MAX_BONES];
} *bones;//[MAX_BONES];
unsigned int numbones;
int warnlimit; //don't spam warnings. this is a loader, not a spammer
@ -924,7 +935,7 @@ static void *GLTF_AccessorToDataUB(gltf_t *gltf, size_t outverts, unsigned int o
static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_accessor *a)
{ //input should only be ubytes||ushorts.
const unsigned int outcomponents = 4;
unsigned char *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o;
boneidx_t *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o;
char *in = a->data;
@ -964,7 +975,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_
for (c = 0; c < ic; c++)
{
v = ((unsigned short*)in)[c];
if (v > 255)
if (v > MAX_BONES)
v = 0;
o[c] = gltf->bonemap[v];
}
@ -974,6 +985,7 @@ static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_
in += a->bytestride;
}
break;
//the spec doesn't require these.
// case 5125: //UNSIGNED_INT
/* case 5126: //FLOAT
while(outverts --> 0)
@ -1117,8 +1129,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
{
qboolean doubleSided;
int alphaMode;
//double alphaCutoff;
double alphaCutoff;
char shader[8192];
char alphaCutoffmodifier[128];
json_t *mat = JSON_FindIndexedChild(gltf->r, "materials", material);
galiasskin_t *ret;
@ -1130,19 +1143,8 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
pbrmr = JSON_FindChild(mat, "pbrMetallicRoughness");
blinn = JSON_FindChild(mat, "extensions.KHR_materials_cmnBlinnPhong");
/* JSON_WarnIfChild(mat, "name");
JSON_WarnIfChild(pbrsg, "diffuseFactor");
JSON_WarnIfChild(pbrsg, "diffuseTexture");
JSON_WarnIfChild(pbrsg, "specularFactor");
JSON_WarnIfChild(pbrsg, "glossinessFactor");
JSON_WarnIfChild(pbrsg, "specularGlossinessTexture");
JSON_WarnIfChild(mat, "normalTexture");
JSON_WarnIfChild(mat, "occlusionTexture");
JSON_WarnIfChild(mat, "emissiveTexture");
JSON_WarnIfChild(mat, "emissiveFactor"); //0,0,0
*/
doubleSided = JSON_GetInteger(mat, "doubleSided", false);
//alphaCutoff = JSON_GetInteger(mat, "alphaCutoff", 0.5);
alphaCutoff = JSON_GetFloat(mat, "alphaCutoff", 0.5);
if (JSON_Equals(mat, "alphaMode", "MASK"))
alphaMode = 1;
else if (JSON_Equals(mat, "alphaMode", "BLEND"))
@ -1162,6 +1164,11 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
else
Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%i", material);
if (alphaMode == 1)
Q_snprintf(alphaCutoffmodifier, sizeof(alphaCutoffmodifier), "#ALPHATEST=>%f", alphaCutoff);
else
*alphaCutoffmodifier = 0;
if (unlit)
{ //if this extension was present, then we don't get ANY lighting info.
int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba
@ -1171,7 +1178,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
"{\n"
"surfaceparm nodlight\n"
"%s"//cull
"program default2d\n" //fixme: there's no gpu skeletal stuff with this prog
"program default2d%s\n" //fixme: there's no gpu skeletal stuff with this prog
"{\n"
"map $diffuse\n"
"%s" //blend
@ -1179,8 +1186,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
"}\n"
"fte_basefactor %f %f %f %f\n"
"}\n",
doubleSided?"cullface disable\n":"",
(alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"",
doubleSided?"cull disable\n":"",
alphaCutoffmodifier,
(alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"",
vertexcolours?"rgbgen vertex\nalphagen vertex\n":"",
JSON_GetFloat(pbrmr, "baseColorFactor.0", 1),
JSON_GetFloat(pbrmr, "baseColorFactor.1", 1),
@ -1204,7 +1212,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
Q_snprintf(shader, sizeof(shader),
"{\n"
"%s"//cull
"program defaultskin#VC\n"
"program defaultskin#VC%s\n"
"{\n"
"map $diffuse\n"
"%s" //blend
@ -1214,20 +1222,21 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
"fte_specularfactor %f %f %f %f\n"
"fte_fullbrightfactor %f %f %f 1.0\n"
"}\n",
doubleSided?"cullface disable\n":"",
(alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"",
doubleSided?"cull disable\n":"",
alphaCutoffmodifier,
(alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"",
vertexcolours?"rgbgen vertex\nalphagen vertex\n":"",
JSON_GetFloat(pbrsg, "diffuseFactor.0", 1),
JSON_GetFloat(pbrsg, "diffuseFactor.1", 1),
JSON_GetFloat(pbrsg, "diffuseFactor.2", 1),
JSON_GetFloat(pbrsg, "diffuseFactor.3", 1),
JSON_GetFloat(pbrsg, "specularFactor.0", 1),
JSON_GetFloat(pbrsg, "specularFactor.0", 1), //FIXME: divide by gl_specular
JSON_GetFloat(pbrsg, "specularFactor.1", 1),
JSON_GetFloat(pbrsg, "specularFactor.2", 1),
JSON_GetFloat(pbrsg, "shininessFactor", 1),
JSON_GetFloat(mat, "emissiveFactor.0", 1),
JSON_GetFloat(mat, "emissiveFactor.1", 1),
JSON_GetFloat(mat, "emissiveFactor.2", 1)
JSON_GetFloat(pbrsg, "shininessFactor", 1), //FIXME: divide by gl_specular_power
JSON_GetFloat(mat, "emissiveFactor.0", 0),
JSON_GetFloat(mat, "emissiveFactor.1", 0),
JSON_GetFloat(mat, "emissiveFactor.2", 0)
);
}
else if (pbrsg)
@ -1238,7 +1247,7 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
Q_snprintf(shader, sizeof(shader),
"{\n"
"%s"//cull
"program defaultskin#VC\n"
"program defaultskin#SG#VC#NOOCCLUDE%s\n"
"{\n"
"map $diffuse\n"
"%s" //blend
@ -1247,9 +1256,11 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
"fte_basefactor %f %f %f %f\n"
"fte_specularfactor %f %f %f %f\n"
"fte_fullbrightfactor %f %f %f 1.0\n"
"bemode rtlight rtlight_sg\n"
"}\n",
doubleSided?"cullface disable\n":"",
(alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"",
doubleSided?"cull disable\n":"",
alphaCutoffmodifier,
(alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"",
vertexcolours?"rgbgen vertex\nalphagen vertex\n":"",
JSON_GetFloat(pbrsg, "diffuseFactor.0", 1),
JSON_GetFloat(pbrsg, "diffuseFactor.1", 1),
@ -1259,9 +1270,9 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
JSON_GetFloat(pbrsg, "specularFactor.1", 1),
JSON_GetFloat(pbrsg, "specularFactor.2", 1),
JSON_GetFloat(pbrsg, "glossinessFactor", 1)*32, //this is fucked.
JSON_GetFloat(mat, "emissiveFactor.0", 1),
JSON_GetFloat(mat, "emissiveFactor.1", 1),
JSON_GetFloat(mat, "emissiveFactor.2", 1)
JSON_GetFloat(mat, "emissiveFactor.0", 0),
JSON_GetFloat(mat, "emissiveFactor.1", 0),
JSON_GetFloat(mat, "emissiveFactor.2", 0)
);
}
else if (pbrmr)
@ -1270,45 +1281,52 @@ static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vert
int mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused
int occ = JSON_GetInteger(mat, "occlusionTexture.index", -1); //.r
//now work around potential lame exporters.
//now work around potential lame exporters (yay dds?).
occ = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", occ);
mrt = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", mrt);
if (occ != mrt && occ != -1) //if its -1 then the mrt should have an unused channel set to 1. however, this isn't guarenteed...
{
occ = -1; //not supported. fixme: support some weird loadtexture channel merging stuff
if (gltf->warnlimit --> 0)
Con_Printf(CON_WARNING"%s: Separate occlusion and metallicRoughness textures are not supported\n", gltf->mod->name);
}
//note: extensions.MSFT_packing_normalRoughnessMetallic.normalRoughnessMetallicTexture.index gives rg=normalxy, b=roughness, .a=metalic
//(would still need an ao map, and probably wouldn't work well as bc3 either)
ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0);
ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, 0);
ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, IF_NOSRGB);
Q_snprintf(shader, sizeof(shader),
"{\n"
"%s"//cull
"program defaultskin#PBR_ORM#VC\n"
"program defaultskin#ORM#VC%s%s\n"
"{\n"
"map $diffuse\n"
"%s" //blend
"%s" //rgbgen
"}\n"
"fte_basefactor %f %f %f %f\n"
"fte_specularfactor 1.0 %f %f 1.0\n"
"fte_specularfactor %f %f %f 1.0\n"
"fte_fullbrightfactor %f %f %f 1.0\n"
"bemode rtlight rtlight_orm\n"
"}\n",
doubleSided?"cullface disable\n":"",
(alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"",
doubleSided?"cull disable\n":"",
(occ==-1)?"#NOOCCLUDE":"",
alphaCutoffmodifier,
(alphaMode==1)?"":(alphaMode==2)?"blendfunc blend\n":"",
vertexcolours?"rgbgen vertex\nalphagen vertex\n":"",
JSON_GetFloat(pbrmr, "baseColorFactor.0", 1),
JSON_GetFloat(pbrmr, "baseColorFactor.1", 1),
JSON_GetFloat(pbrmr, "baseColorFactor.2", 1),
JSON_GetFloat(pbrmr, "baseColorFactor.3", 1),
JSON_GetFloat(pbrmr, "metallicFactor", 1),
JSON_GetFloat(pbrmr, "roughnessFactor", 1),
JSON_GetFloat(mat, "emissiveFactor.0", 1),
JSON_GetFloat(mat, "emissiveFactor.1", 1),
JSON_GetFloat(mat, "emissiveFactor.2", 1)
JSON_GetFloat(mat, "occlusionTexture.strength", 1),
JSON_GetFloat(pbrmr, "metallicFactor", 1),
JSON_GetFloat(pbrmr, "roughnessFactor", 1),
JSON_GetFloat(mat, "emissiveFactor.0", 0),
JSON_GetFloat(mat, "emissiveFactor.1", 0),
JSON_GetFloat(mat, "emissiveFactor.2", 0)
);
}
ret->frame->texnums.bump = GLTF_LoadTexture(gltf, JSON_GetInteger(mat, "normalTexture.index", -1), IF_NOSRGB|IF_TRYBUMP);
@ -1374,9 +1392,14 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double
surf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf));
surf->surfaceid = meshidx;
surf->contents = FTECONTENTS_BODY;
surf->csurface.flags = 0;
surf->surfaceid = surf->contents = JSON_GetInteger(prim, "extras.fte.surfaceid", meshidx);
surf->contents = JSON_GetInteger(prim, "extras.fte.contents", FTECONTENTS_BODY);
surf->csurface.flags = JSON_GetInteger(prim, "extras.fte.surfaceflags", 0);
surf->geomset = JSON_GetInteger(prim, "extras.fte.geomset", ~0u);
surf->geomid = JSON_GetInteger(prim, "extras.fte.geomid", 0);
surf->mindist = JSON_GetInteger(prim, "extras.fte.mindist", 0);
surf->maxdist = JSON_GetInteger(prim, "extras.fte.maxdist", 0);
surf->shares_bones = gltf->numsurfaces;
surf->shares_verts = gltf->numsurfaces;
JSON_ReadBody(meshname, surf->surfacename, sizeof(surf->surfacename));
@ -1474,6 +1497,12 @@ static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double
surf->numskins = 1;
surf->ofsskins = GLTF_LoadMaterial(gltf, mat, surf->ofs_rgbaub||surf->ofs_rgbaf);
if (!tang.data)
{
modfuncs->AccumulateTextureVectors(surf->ofs_skel_xyz, surf->ofs_st_array, surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->ofs_indexes, surf->numindexes, !norm.data);
modfuncs->NormaliseTextureVectors(surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->numverts, !norm.data);
}
gltf->numsurfaces++;
surf->nextsurf = mod->meshinfo;
mod->meshinfo = surf;
@ -1551,10 +1580,16 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16],
int skinidx;
struct gltfbone_s *b;
if (nodeidx < 0 || nodeidx >= gltf->numbones)
{
Con_Printf(CON_WARNING"%s: Invalid node index %i\n", gltf->mod->name, nodeidx);
return false;
}
node = JSON_FindIndexedChild(gltf->r, "nodes", nodeidx);
if (!node)
{
Con_Printf(CON_WARNING"%s: Invalid node index %i\n", gltf->mod->name, nodeidx);
return false;
}
b = &gltf->bones[nodeidx];
b->parent = parentidx;
@ -1638,7 +1673,7 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16],
inversef = inverse.data;
if (inverse.componentType != 5126/*FLOAT*/ || inverse.type != ((4<<8) | 4)/*mat4x4*/)
inverse.count = 0;
for (j = 0; j < countof(gltf->bonemap); j++, inversef+=inverse.bytestride/sizeof(float))
for (j = 0; j < MAX_BONES; j++, inversef+=inverse.bytestride/sizeof(float))
{
int b = JSON_GetIndexedInteger(joints, j, -1);
if (b < 0)
@ -1705,7 +1740,7 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16],
GLTF_ProcessNode(gltf, JSON_GetInteger(c, NULL, -1), b->amatrix, nodeidx, isjoint);
}
JSON_FlagAsUsed(node, "camera");
b->camera = JSON_GetInteger(node, "camera", -1);
JSON_WarnIfChild(node, "weights", &gltf->warnlimit); //default value for morph weight animations
JSON_WarnIfChild(node, "extensions", &gltf->warnlimit);
@ -1798,8 +1833,8 @@ static void LerpAnimData(gltf_t *gltf, struct gltf_animsampler *samp, float time
struct gltf_accessor *in = &samp->input;
struct gltf_accessor *out = &samp->output;
t1 = t2 = Anim_GetTime(in, 0);
for (f2 = 1 ; f2 < in->count; f2++)
t1 = t2 = Anim_GetTime(in, f1);
for (f2 = 1; f2 < in->count; f2++)
{
t2 = Anim_GetTime(in, f2);
if (t2 > time)
@ -1808,19 +1843,35 @@ static void LerpAnimData(gltf_t *gltf, struct gltf_animsampler *samp, float time
f1 = f2;
}
//assume linear
if (f1==f2 || t1==t2)
{
Anim_GetVal(out, f1, result, elems);
return;
if (time <= t1)
{ //if before the first time, clamp it.
w1 = 1;
w2 = 0;
}
else if (time >= t2)
{ //if after tha last frame we could find, clamp it to the last.
w1 = 0;
w2 = 1;
}
else
{ //assume linear
w2 = (time-t1)/(t2-t1);
// if (1) //step it. it'll still get lerped though. :(
// w2 = (w2>0.5)?1:0;
w1 = 1-w2;
}
w2 = (time-t1)/(t2-t1);
w1 = 1-w2;
Anim_GetVal(out, f1, v1, elems);
Anim_GetVal(out, f2, v2, elems);
for (c = 0; c < elems; c++)
result[c] = v1[c]*w1 + w2*v2[c];
if (w1 >= 1)
Anim_GetVal(out, f1, result, elems);
else if (w2 >= 1)
Anim_GetVal(out, f2, result, elems);
else
{
Anim_GetVal(out, f1, v1, elems);
Anim_GetVal(out, f2, v2, elems);
for (c = 0; c < elems; c++)
result[c] = v1[c]*w1 + w2*v2[c];
}
}
static void GLTF_RemapBone(gltf_t *gltf, int *nextidx, int b)
@ -1877,6 +1928,20 @@ static void GLTF_RewriteBoneTree(gltf_t *gltf)
//we do NOT supported nested nodes right now...
static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, void *buffer, size_t buffersize)
{
static struct
{
const char *name;
qboolean supported; //unsupported extensions don't really need to be listed, but they do prevent warnings from unkown-but-used extensions
} extensions[] =
{
{"KHR_materials_pbrSpecularGlossiness", true},
//draft {"KHR_materials_cmnBlinnPhong", true},
{"KHR_materials_unlit", true},
{"KHR_texture_transform", false},
{"KHR_draco_mesh_compression", false},
{"MSFT_texture_dds", true},
{"MSFT_packing_occlusionRoughnessMetallic", true},
};
gltf_t gltf;
int pos=0, j, k;
json_t *scene, *n, *anim;
@ -1888,6 +1953,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
unsigned int numframegroups = 0;
float *baseframe;
memset(&gltf, 0, sizeof(gltf));
gltf.bonemap = malloc(sizeof(*gltf.bonemap)*MAX_BONES);
gltf.bones = malloc(sizeof(*gltf.bones)*MAX_BONES);
memset(gltf.bones, 0, sizeof(*gltf.bones)*MAX_BONES);
gltf.r = JSON_Parse(NULL, mod->name, NULL, json, &pos, jsonsize);
gltf.mod = mod;
gltf.buffers[0].data = buffer;
@ -1909,23 +1977,33 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
{
char extname[256];
JSON_ReadBody(n, extname, sizeof(extname));
Con_Printf(CON_ERROR "%s: Required gltf2 extension \"%s\" not supported\n", mod->name, extname);
for (j = 0; j < countof(extensions); j++)
{
if (!strcmp(extname, extensions[j].name))
break;
}
if (j==countof(extensions) || !extensions[j].supported)
Con_Printf(CON_ERROR "%s: Required gltf2 extension \"%s\" not supported\n", mod->name, extname);
JSON_Destroy(gltf.r);
return false;
goto abort;
}
for(n = JSON_FindIndexedChild(gltf.r, "extensionsUsed", 0); n; n = n->sibling)
{ //must be a superset of the above.
char extname[256];
JSON_ReadBody(n, extname, sizeof(extname));
if (!strcmp(extname, "KHR_materials_pbrSpecularGlossiness"))
;
else if (!strcmp(extname, "KHR_texture_transform"))
;
else
for (j = 0; j < countof(extensions); j++)
{
if (!strcmp(extname, extensions[j].name))
break;
}
if (j==countof(extensions) || !extensions[j].supported)
Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" not known\n", mod->name, extname);
}
VectorClear(mod->maxs);
VectorClear(mod->mins);
//we don't really care about cameras.
JSON_FlagAsUsed(gltf.r, "cameras");
@ -1933,16 +2011,21 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
memset(&rootmatrix, 0, sizeof(rootmatrix));
#if 1 //transform from gltf to quake. mostly only needed for the base pose.
rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = 1; rootmatrix[15] = 1;
rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = GLTFSCALE; rootmatrix[15] = 1;
#else
rootmatrix[0] = rootmatrix[5] = rootmatrix[10] = 1; rootmatrix[15] = 1;
#endif
for (j = 0; j < countof(gltf.bones); j++)
for (j = 0; ; j++)
{
n = JSON_FindIndexedChild(gltf.r, "nodes", j);
if (!n)
break;
if (j == MAX_BONES)
{
Con_Printf(CON_WARNING"%s: too many nodes (max %i)\n", mod->name, MAX_BONES);
break;
}
if (!JSON_ReadBody(JSON_FindChild(n, "name"), gltf.bones[j].name, sizeof(gltf.bones[j].name)))
{
if (n)
@ -1950,6 +2033,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
else
Q_snprintf(gltf.bones[j].name, sizeof(gltf.bones[j].name), "bone%i", j);
}
gltf.bones[j].camera = -1;
gltf.bones[j].parent = -1;
gltf.bones[j].amatrix[0] = gltf.bones[j].amatrix[5] = gltf.bones[j].amatrix[10] = gltf.bones[j].amatrix[15] = 1;
gltf.bones[j].inverse[0] = gltf.bones[j].inverse[5] = gltf.bones[j].inverse[10] = gltf.bones[j].inverse[15] = 1;
@ -1966,8 +2050,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
if (!n)
break;
n->used = true;
if (!GLTF_ProcessNode(&gltf, JSON_GetInteger(n, NULL, -1), rootmatrix, -1, false))
break;
// if (!
GLTF_ProcessNode(&gltf, JSON_GetInteger(n, NULL, -1), rootmatrix, -1, false);
// break;
}
GLTF_RewriteBoneTree(&gltf);
@ -1979,6 +2064,9 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
Q_strlcpy(bone[j].name, gltf.bones[j].name, sizeof(bone[j].name));
bone[j].parent = gltf.bones[j].parent;
if (gltf.bones[j].camera >= 0 && !mod->camerabone)
mod->camerabone = j+1;
for(k = 0; k < 12; k++)
{
baseframe[j*12+k] = gltf.bones[j].amatrix[k];
@ -1990,6 +2078,10 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
numframegroups++;
if (numframegroups)
{
struct
{
struct gltf_animsampler rot,scale,trans;
} *b = malloc(sizeof(*b)*gltf.numbones);
framegroups = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*framegroups)*numframegroups);
for (k = 0; k < numframegroups; k++)
{
@ -1999,11 +2091,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
json_t *samps = JSON_FindChild(anim, "samplers");
int f, l;
float maxtime = 0;
struct
{
struct gltf_animsampler rot,scale,trans;
} b[MAX_BONES];
memset(b, 0, sizeof(b));
memset(b, 0, sizeof(*b)*gltf.numbones);
if (!JSON_ReadBody(JSON_FindChild(anim, "name"), fg->name, sizeof(fg->name)))
{
@ -2041,14 +2129,14 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
else if (gltf.warnlimit --> 0)
{ //these are unsupported
if (JSON_Equals(path, NULL, "weights")) //morph weights
Con_Printf("%s: morph animations are not supported\n", mod->name);
Con_Printf(CON_WARNING"%s: morph animations are not supported\n", mod->name);
else
Con_Printf("%s: undocumented animation type\n", mod->name);
}
}
//TODO: make a guess at the framerate according to sampler intervals
fg->rate = 30;
fg->rate = 60;
fg->numposes = max(1, maxtime*fg->rate);
if (maxtime)
fg->rate = fg->numposes/maxtime; //fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact).
@ -2089,13 +2177,14 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
if (gltf.bones[j].parent < 0)
{ //rotate any root bones from gltf to quake's orientation.
float fnar[12];
static float toquake[12]={0,0,1,0,1,0,0,0,0,1,0,0};
static float toquake[12]={0,0,GLTFSCALE,0,GLTFSCALE,0,0,0,0,GLTFSCALE,0,0};
memcpy(fnar, bonematrix, sizeof(fnar));
modfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix);
}
}
}
}
free(b);
}
for(surf = mod->meshinfo; surf; surf = surf->nextsurf)
@ -2111,13 +2200,19 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
surf->geomset = ~0; //invalid set = always visible. FIXME: set this according to scene numbers?
surf->geomid = 0;
}
VectorScale(mod->mins, GLTFSCALE, mod->mins);
VectorScale(mod->maxs, GLTFSCALE, mod->maxs);
if (!mod->meshinfo)
Con_Printf("%s: Doesn't contain any meshes...\n", mod->name);
JSON_WarnUnused(gltf.r, &gltf.warnlimit);
}
else
Con_Printf("%s: unsupported gltf version (%.2f)\n", mod->name, gltfver);
abort:
JSON_Destroy(gltf.r);
free(gltf.bones);
free(gltf.bonemap);
mod->type = mod_alias;

View File

@ -1,7 +1,7 @@
FTE_MULTIPROGS
void() init; //called before ents are available (spawn() will fail, but you can use addprogs() here)
void() initents; //called just before worldspawn, after ents are spawnable spawn() works but addprogs() may fail)
void() initents; //called just before worldspawn, after ents are spawnable (spawn() works but addprogs() may fail)
float thisprogs; //set by the engine. main progs will always be 0, addons will be non-zero and unique.
float(string modname) addprogs = #202; //load another progs as well - may fail if too many fields are added.
@ -54,4 +54,4 @@ initents permits the use of spawn(). spawned ents will be *after* world, even th
using addprogs later than the main progs' init function can fail if too many(any) new fields are defined.
note that a great use of this extension is to add frikbots to any mod without modding it.
externvalue and externset do work for the current progs too. it can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient).
externvalue and externset do work for the current progs too. it can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient).