diff --git a/engine/client/client.h b/engine/client/client.h index a516e10f0..09b0d77c5 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -345,6 +345,7 @@ typedef struct dlight_s } face [6]; int style; //multiply by style values if > 0 float fov; //spotlight + float nearclip; //for spotlights... struct dlight_s *next; } dlight_t; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 27190c475..af302032f 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1158,6 +1158,9 @@ static void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globa case lfield_fov: l->fov = G_FLOAT(OFS_PARM2); break; + case lfield_nearclip: + l->nearclip = G_FLOAT(OFS_PARM2); + break; case lfield_corona: l->corona = G_FLOAT(OFS_PARM2); break; @@ -1245,6 +1248,9 @@ static void QCBUILTIN PF_R_DynamicLight_Get(pubprogfuncs_t *prinst, struct globa case lfield_fov: G_FLOAT(OFS_RETURN) = l->fov; break; + case lfield_nearclip: + G_FLOAT(OFS_PARM2) = l->nearclip; + break; case lfield_corona: G_FLOAT(OFS_RETURN) = l->corona; break; @@ -1978,6 +1984,13 @@ nogameaccess: *r = r_refdef.afov; break; + case VF_SKYROOM_CAMERA: + if (r_refdef.skyroom_enabled) + VectorCopy(r_refdef.skyroom_pos, r); + else + VectorClear(r); //not really correct, but no other way to really signal this. -0? yuck. + break; + case VF_ORIGIN: if (csqc_nogameaccess && prinst == csqc_world.progs) goto nogameaccess; @@ -2178,6 +2191,11 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ r_refdef.dirty |= RDFD_FOV; break; + case VF_SKYROOM_CAMERA: + r_refdef.skyroom_enabled = true; + VectorCopy(p, r_refdef.skyroom_pos); + break; + case VF_ORIGIN: VectorCopy(p, r_refdef.vieworg); if (csqc_playerview) diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 4b711bb34..7eccb4fb2 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -3990,6 +3990,10 @@ void Surf_BuildModelLightmaps (model_t *m) dst[2] = src[2]; } break; + case PTI_RGB565: + for (; src < stop; dst += 2, src += 3) + *(unsigned short*)dst = ((src[0]>>3)<<11)|((src[1]>>2)<<5)|((src[2]>>3)<<0); + break; case PTI_L8: for (; src < stop; dst += 1, src += 3) { diff --git a/engine/client/render.h b/engine/client/render.h index f8227118e..3636ea5d6 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -289,6 +289,9 @@ typedef struct fogstate_t globalfog; float hdr_value; + vec3_t skyroom_pos; /*the camera position for sky rooms*/ + qboolean skyroom_enabled; /*whether a skyroom position is defined*/ + pxrect_t pxrect; /*vrect, but in pixels rather than virtual coords*/ qboolean externalview; /*draw external models and not viewmodels*/ int recurse; /*in a mirror/portal/half way through drawing something else*/ diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 3f26c95a2..c50a3114e 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -898,8 +898,8 @@ void Renderer_Init(void) Cvar_Register (&r_telestyle, GRAPHICALNICETIES); Cvar_Register (&r_wireframe, GRAPHICALNICETIES); Cvar_Register (&r_wireframe_smooth, GRAPHICALNICETIES); - Cvar_Register (&r_outline, GRAPHICALNICETIES); - Cvar_Register (&r_outline_width, GRAPHICALNICETIES); +// Cvar_Register (&r_outline, GRAPHICALNICETIES); +// Cvar_Register (&r_outline_width, GRAPHICALNICETIES); Cvar_Register (&r_refract_fbo, GRAPHICALNICETIES); Cvar_Register (&r_refractreflect_scale, GRAPHICALNICETIES); Cvar_Register (&r_postprocshader, GRAPHICALNICETIES); @@ -2734,7 +2734,7 @@ qbyte *R_MarkLeaves_Q1 (qboolean getvisonly) vis = cvis[portal] = r_refdef.forcedvis; r_oldviewcluster = -1; - r_oldviewcluster2 = -1; + r_oldviewcluster2 = -2; } else { @@ -2749,7 +2749,7 @@ qbyte *R_MarkLeaves_Q1 (qboolean getvisonly) else { r_oldviewcluster = -1; - r_oldviewcluster2 = -1; + r_oldviewcluster2 = -2; } if (r_novis.ival) @@ -2760,7 +2760,7 @@ qbyte *R_MarkLeaves_Q1 (qboolean getvisonly) memset (curframevis[portal].buffer, 0xff, curframevis[portal].buffersize); r_oldviewcluster = -1; - r_oldviewcluster2 = -1; + r_oldviewcluster2 = -2; } else { diff --git a/engine/client/snd_sdl.c b/engine/client/snd_sdl.c index f243c24d4..0ac0fbb38 100644 --- a/engine/client/snd_sdl.c +++ b/engine/client/snd_sdl.c @@ -61,8 +61,11 @@ static int (SDLCALL *SDL_CloseAudioDevice) (SDL_AudioDeviceID fd); static int (SDLCALL *SDL_GetNumAudioDevices) (int iscapture); static const char *(SDLCALL *SDL_GetAudioDeviceName) (int index, int iscapture); static const char *(SDLCALL *SDL_GetError) (void); -#if SDL_VERSION_ATLEAST(2,0,5) +#if SDL_VERSION_ATLEAST(2,0,4) +static int (SDLCALL *SDL_QueueAudio) (SDL_AudioDeviceID dev, const void *data, uint32_t len); static uint32_t (SDLCALL *SDL_GetQueuedAudioSize) (SDL_AudioDeviceID dev); +#endif +#if SDL_VERSION_ATLEAST(2,0,5) static uint32_t (SDLCALL *SDL_DequeueAudio) (SDL_AudioDeviceID dev, void *data, uint32_t len); #endif static dllfunction_t sdl_funcs[] = @@ -77,8 +80,11 @@ static dllfunction_t sdl_funcs[] = {(void*)&SDL_GetNumAudioDevices, "SDL_GetNumAudioDevices"}, {(void*)&SDL_GetAudioDeviceName, "SDL_GetAudioDeviceName"}, {(void*)&SDL_GetError, "SDL_GetError"}, -#if SDL_VERSION_ATLEAST(2,0,5) +#if SDL_VERSION_ATLEAST(2,0,4) + {(void*)&SDL_QueueAudio, "SDL_QueueAudio"}, {(void*)&SDL_GetQueuedAudioSize, "SDL_GetQueuedAudioSize"}, +#endif +#if SDL_VERSION_ATLEAST(2,0,5) {(void*)&SDL_DequeueAudio, "SDL_DequeueAudio"}, #endif {NULL, NULL} @@ -154,7 +160,7 @@ static void SSDL_Shutdown(soundcardinfo_t *sc) #endif sc->sn.buffer = NULL; } -static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc) +static unsigned int SSDL_Callback_GetDMAPos(soundcardinfo_t *sc) { sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes; return sc->sn.samplepos; @@ -162,7 +168,7 @@ static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc) //this function is called from inside SDL. //transfer the 'dma' buffer into the buffer it requests. -static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len) +static void VARGS SSDL_Callback_Paint(void *userdata, qbyte *stream, int len) { soundcardinfo_t *sc = userdata; @@ -194,7 +200,7 @@ static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len) #endif } -static void *SSDL_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) +static void *SSDL_Callback_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) { #if SDL_MAJOR_VERSION >= 2 SDL_LockAudioDevice(sc->audio_fd); @@ -205,7 +211,7 @@ static void *SSDL_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) return sc->sn.buffer; } -static void SSDL_UnlockBuffer(soundcardinfo_t *sc, void *buffer) +static void SSDL_Callback_UnlockBuffer(soundcardinfo_t *sc, void *buffer) { #if SDL_MAJOR_VERSION >= 2 SDL_UnlockAudioDevice(sc->audio_fd); @@ -214,11 +220,42 @@ static void SSDL_UnlockBuffer(soundcardinfo_t *sc, void *buffer) #endif } -static void SSDL_Submit(soundcardinfo_t *sc, int start, int end) +static void SSDL_Callback_Submit(soundcardinfo_t *sc, int start, int end) { //SDL will call SSDL_Paint to paint when it's time, and the sound buffer is always there... } +#if SDL_VERSION_ATLEAST(2,0,4) +static unsigned int SSDL_Queue_GetDMAPos(soundcardinfo_t *sc) +{ //keep proper track of how much data has actually been sent to the audio device. + //note that SDL may have already submitted more than this to the physical device. + //note: if we don't mix enough data then sdl will mix 0s for us. + uint32_t queued = SDL_GetQueuedAudioSize(sc->audio_fd); + extern cvar_t _snd_mixahead; + int ahead = (_snd_mixahead.value*sc->sn.speed) - (queued / (sc->sn.samplebytes*sc->sn.numchannels)); + if (ahead < 0) + ahead = 0; //never behind + sc->samplequeue = -1; //return value is a desired timestamp + return sc->sn.samplepos + ahead*sc->sn.numchannels; +} +static void *SSDL_Queue_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) +{ //queuing uses private memory, so no need to lock + *sampidx = 0; //don't bother ringing it. + return sc->sn.buffer; +} +static void SSDL_Queue_UnlockBuffer(soundcardinfo_t *sc, void *buffer) +{ //nor a need to unlock +} + +static void SSDL_Queue_Submit(soundcardinfo_t *sc, int start, int end) +{ + int bytecount = (end-start)*sc->sn.samplebytes*sc->sn.numchannels; + SDL_QueueAudio(sc->audio_fd, sc->sn.buffer, bytecount); + + sc->sn.samplepos += bytecount/sc->sn.samplebytes; +} +#endif + static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename) { SDL_AudioSpec desired, obtained; @@ -234,7 +271,12 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename) desired.freq = sc->sn.speed; desired.channels = sc->sn.numchannels; //fixme! desired.samples = 0x0200; //'Good values seem to range between 512 and 8192 inclusive, depending on the application and CPU speed.' - desired.callback = (void*)SSDL_Paint; +#if SDL_VERSION_ATLEAST(2,0,4) + if (!snd_mixerthread.ival) + desired.callback = NULL; + else +#endif + desired.callback = (void*)SSDL_Callback_Paint; desired.userdata = sc; memcpy(&obtained, &desired, sizeof(obtained)); @@ -299,26 +341,44 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename) break; } -#ifdef SELFPAINT - sc->selfpainting = true; -#endif - Con_DPrintf("channels: %i\n", sc->sn.numchannels); Con_DPrintf("Speed: %i\n", sc->sn.speed); Con_DPrintf("Samplebits: %i\n", sc->sn.samplebytes*8); Con_DPrintf("SDLSamples: %i (low for latency)\n", obtained.samples); Con_DPrintf("FakeSamples: %i\n", sc->sn.samples); -#ifndef SELFPAINT - sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes); -#endif Con_DPrintf("Got sound %i-%i\n", obtained.freq, obtained.format); - sc->Lock = SSDL_LockBuffer; - sc->Unlock = SSDL_UnlockBuffer; - sc->Submit = SSDL_Submit; - sc->Shutdown = SSDL_Shutdown; - sc->GetDMAPos = SSDL_GetDMAPos; +#if SDL_VERSION_ATLEAST(2,0,4) + if (!obtained.callback) + { + sc->Lock = SSDL_Queue_LockBuffer; + sc->Unlock = SSDL_Queue_UnlockBuffer; + sc->Submit = SSDL_Queue_Submit; + sc->Shutdown = SSDL_Shutdown; + sc->GetDMAPos = SSDL_Queue_GetDMAPos; + + sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes); + + Con_DPrintf("Using SDL audio queues\n"); + } + else +#endif + { + sc->Lock = SSDL_Callback_LockBuffer; + sc->Unlock = SSDL_Callback_UnlockBuffer; + sc->Submit = SSDL_Callback_Submit; + sc->Shutdown = SSDL_Shutdown; + sc->GetDMAPos = SSDL_Callback_GetDMAPos; + +#ifdef SELFPAINT + sc->selfpainting = true; + Con_DPrintf("Using SDL audio threading\n"); +#else + sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes); + Con_DPrintf("Using SDL audio callbacks\n"); +#endif + } #if SDL_MAJOR_VERSION >= 2 SDL_PauseAudioDevice(sc->audio_fd, 0); diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 50bee2644..5ef95638c 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -755,7 +755,7 @@ void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname) // ======================================================================= //friendly way to crash, including stack traces. should help reduce gdb use. -#ifdef __linux__ /*should probably be GNUC but whatever*/ +#if defined(__linux__) && defined(__GNUC__) /*should probably be GNUC but whatever*/ #include #ifdef __i386__ #include @@ -927,7 +927,7 @@ int main (int c, const char **v) } #endif -#ifdef __linux__ +#if defined(__linux__) && defined(__GNUC__) if (!COM_CheckParm("-nodumpstack")) { struct sigaction act; diff --git a/engine/client/view.c b/engine/client/view.c index d94c68669..cb830aba3 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1502,6 +1502,8 @@ void V_ClearRefdef(playerview_t *pv) r_refdef.drawsbar = (cl.intermissionmode == IM_NONE); r_refdef.flags = 0; + r_refdef.skyroom_enabled = false; + r_refdef.areabitsknown = false; // memset(r_refdef.postprocshader, 0, sizeof(r_refdef.postprocshader)); diff --git a/engine/common/fs.c b/engine/common/fs.c index c74d101e9..06adf33d4 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3153,7 +3153,7 @@ const gamemode_info_t gamemode_info[] = { //because we can. quakespasm is hopefully close enough... {"-fitz", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset spasm\n",{"id1"}, "NetQuake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, //because we can - {"-tenebrae", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},QCFG"fps_preset tenebrae\n",{"id1","qw","*fte"},"Tenebrae", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, + {"-tenebrae", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset tenebrae\n",{"id1","qw","*fte"},"Tenebrae", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, //quake's mission packs should not be favoured over the base game nor autodetected //third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content. diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 2eeacef19..6cb2060da 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -38,6 +38,7 @@ cvar_t q3bsp_surf_meshcollision_flag = CVARD("q3bsp_surf_meshcollision_flag", "0x80000000", "The surfaceparm flag(s) that enables q3bsp trisoup collision"); cvar_t q3bsp_surf_meshcollision_force = CVARD("q3bsp_surf_meshcollision_force", "0", "Force mesh-based collisions on all q3bsp trisoup surfaces."); cvar_t q3bsp_mergeq3lightmaps = CVARD("q3bsp_mergedlightmaps", "16", "Specifies the maximum number of lightmaps that may be merged for performance reasons. Unfortunately this breaks tcgen on lightmap passes - if you care, set this to 1."); +cvar_t q3bsp_bihtraces = CVARFD("_q3bsp_bihtraces", "0", CVAR_RENDERERLATCH, "Uses runtime-generated bih collision culling for faster traces."); #if Q3SURF_NODRAW != TI_NODRAW #error "nodraw isn't constant" @@ -60,6 +61,9 @@ static qboolean CM_NativeTrace(model_t *model, int forcehullnum, framestate_t *f static unsigned int CM_NativeContents(struct model_s *model, int hulloverride, framestate_t *framestate, vec3_t axis[3], vec3_t p, vec3_t mins, vec3_t maxs); static unsigned int Q2BSP_PointContents(model_t *mod, vec3_t axis[3], vec3_t p); static int CM_PointCluster (model_t *mod, vec3_t p); +struct cminfo_s; +static struct bihnode_s *CM_BuildBIH (model_t *mod, struct cminfo_s *prv); +static unsigned int CM_PointContentsBIH (fte_restrict const struct bihnode_s *node, fte_restrict const vec3_t p); #endif float RadiusFromBounds (vec3_t mins, vec3_t maxs) @@ -126,7 +130,7 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s) } } -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs) +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs) { int i; vec_t val; @@ -411,6 +415,8 @@ typedef struct cminfo_s q3cface_t *faces; int numfaces; #endif + + struct bihnode_s *bihnodes; } cminfo_t; static q2mapsurface_t nullsurface; @@ -426,7 +432,7 @@ void CM_InitBoxHull (void); static void CM_FinalizeBrush(q2cbrush_t *brush); static void FloodAreaConnections (cminfo_t *prv); -qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2) +qboolean BoundsIntersect (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2) { return (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] && maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]); @@ -695,7 +701,7 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver { int i, j; int axis, dir; - vec3_t normal, mins, maxs; + vec3_t normal; float d, dist; mplane_t mainplane; vec3_t vec, vec2; @@ -745,9 +751,9 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver brushplanes[numbrushplanes].dist = mainplane.dist; numbrushplanes++; // calculate mins & maxs - ClearBounds( mins, maxs ); + ClearBounds( facet->absmins, facet->absmaxs ); for( i = 0; i < numverts; i++ ) - AddPointToBounds( verts[i], mins, maxs ); + AddPointToBounds( verts[i], facet->absmins, facet->absmaxs ); // add the axial planes for( axis = 0; axis < 3; axis++ ) @@ -765,9 +771,9 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver VectorClear( normal ); normal[axis] = dir; if( dir == 1 ) - dist = maxs[axis]; + dist = facet->absmaxs[axis]; else - dist = -mins[axis]; + dist = -facet->absmins[axis]; VectorCopy( normal, brushplanes[numbrushplanes].normal ); brushplanes[numbrushplanes].dist = dist; numbrushplanes++; @@ -4593,6 +4599,10 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole mod->terrain = Mod_LoadTerrainInfo(mod, loadname, false); #endif + if (q3bsp_bihtraces.ival) + prv->bihnodes = CM_BuildBIH(mod, prv); + else + prv->bihnodes = NULL; return &prv->cmodels[0]; } @@ -4897,38 +4907,43 @@ int CM_PointContents (model_t *mod, vec3_t p) if (!mod) // map not loaded return 0; - i = CM_PointLeafnum_r (mod, p, mod->hulls[0].firstclipnode); - - if (mod->fromgame == fg_quake2) - contents = mod->leafs[i].contents; //q2 is simple. + if (mod->fromgame != fg_quake2 && ((cminfo_t*)mod->meshinfo)->bihnodes) + contents = CM_PointContentsBIH(((cminfo_t*)mod->meshinfo)->bihnodes, p); else { - leaf = &mod->leafs[i]; + i = CM_PointLeafnum_r (mod, p, mod->hulls[0].firstclipnode); - // if ( leaf->contents & CONTENTS_NODROP ) { - // contents = CONTENTS_NODROP; - // } else { - contents = 0; - // } - - for (i = 0; i < leaf->numleafbrushes; i++) + if (mod->fromgame == fg_quake2) + contents = mod->leafs[i].contents; //q2 is simple. + else { - brush = prv->leafbrushes[leaf->firstleafbrush + i]; + leaf = &mod->leafs[i]; - // check if brush actually adds something to contents - if ( (contents & brush->contents) == brush->contents ) { - continue; - } + // if ( leaf->contents & CONTENTS_NODROP ) { + // contents = CONTENTS_NODROP; + // } else { + contents = 0; + // } - brushside = brush->brushside; - for ( j = 0; j < brush->numsides; j++, brushside++ ) + for (i = 0; i < leaf->numleafbrushes; i++) { - if ( PlaneDiff (p, brushside->plane) > 0 ) - break; - } + brush = prv->leafbrushes[leaf->firstleafbrush + i]; - if (j == brush->numsides) - contents |= brush->contents; + // check if brush actually adds something to contents + if ( (contents & brush->contents) == brush->contents ) { + continue; + } + + brushside = brush->brushside; + for ( j = 0; j < brush->numsides; j++, brushside++ ) + { + if ( PlaneDiff (p, brushside->plane) > 0 ) + break; + } + + if (j == brush->numsides) + contents |= brush->contents; + } } } #ifdef TERRAIN @@ -5611,17 +5626,60 @@ static void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1, { int i, j; mplane_t *plane; - vec3_t ofs; - float dist; - float d1, maxdist; + vec3_t ofs, ofs2; + float dist, thickness; + float d1; q2cbrushside_t *side; if (!brush->numsides) return; - maxdist = -9999; + i = 0; //front plane + { + side = brush->brushside+i; + plane = side->plane; - for (i=0 ; inumsides ; i++) + switch(trace_shape) + { + default: + case shape_isbox: + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j], ofs2[j] = mins[j]; + else + ofs[j] = mins[j], ofs2[j] = maxs[j]; + } + + dist = DotProduct (ofs, plane->normal); + thickness = DotProduct (ofs2, plane->normal)-dist; + dist = plane->dist - dist; + break; + case shape_iscapsule: + dist = DotProduct(trace_up, plane->normal); + thickness = dist*(trace_capsulesize[(dist<0)?2:1]) + trace_capsulesize[0]*2; + dist = dist*(trace_capsulesize[(dist<0)?1:2]) - trace_capsulesize[0]; + dist = plane->dist - dist; + break; + case shape_ispoint: + dist = plane->dist; + break; + } + + d1 = DotProduct (p1, plane->normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + //point is behind the front plane, so no real intersection. + if (thickness < 0.25) + thickness = 0.25; //FIXME: patches should probably be infinitely thin, but that makes stuff messy. + if (d1 < -thickness) + return; + } + + for (i=1 ; inumsides ; i++) { side = brush->brushside+i; plane = side->plane; @@ -5657,15 +5715,8 @@ static void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1, // if completely in front of face, no intersection if (d1 > 0) return; - - if (side->surface && d1 > maxdist) - maxdist = d1; } -// FIXME - if (maxdist < -0.25) - return; // deep inside the patch - // inside this patch trace->startsolid = trace->allsolid = true; trace->contents = brush->contents; @@ -5951,6 +6002,386 @@ return; CM_RecursiveHullCheck (mod, node->childnum[side^1], midf, p2f, mid, p2); } +struct bihnode_s +{ + //in a bih tree there are two values per node instead of a kd-tree's single midpoint + //this allows the two sides to overlap, which prevents the need to chop large objects into multiple leafs + //(it also allows gaps in the middle, which can further skip recursion) + enum { + BIH_X, + BIH_Y, + BIH_Z, + BIH_BRUSH, + BIH_PATCHBRUSH, + } type; + union + { + struct{ + vec3_t temp_mins; + vec3_t temp_maxs; + int firstchild; + float cmin[2]; + float cmax[2]; + }; + union bihdata_s{ + q2cbrush_t *brush; + q2cbrush_t *patchbrush; + } data; + }; +}; +struct bihbox_s { + vec3_t min; + vec3_t max; +}; +struct bihtrace_s +{ + struct bihbox_s bounds; + struct bihbox_s size; + vec3_t expand; + qboolean negativedir[3]; + + vec_t *startpos; //bounds.[min|max] + vec3_t totalmove; + vec_t *endpos; //bounds.[min|max] + trace_t *trace; +}; +static void CM_RecursiveBIHTrace (fte_restrict struct bihtrace_s *tr, fte_restrict const struct bihnode_s *node, fte_restrict struct bihbox_s *movesubbounds, fte_restrict struct bihbox_s *nodebox) +{ + //if the tree were 1d, we wouldn't need to be so careful with the bounds, but if the trace is long then we want to avoid hitting all surfaces within that entire-map-encompassing move aabb + if (node->type > BIH_Z) + { //leaf + if (node->type == BIH_BRUSH) + { + q2cbrush_t *b = node->data.brush; + if (b->contents & trace_contents) + if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs)) + CM_ClipBoxToBrush (tr->size.min, tr->size.max, tr->startpos, tr->endpos, tr->trace, b); + } + else //if (node->type == BIH_PATCHBRUSH) + { + q2cbrush_t *b = node->data.patchbrush; + if (b->contents & trace_contents) + if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs)) + CM_ClipBoxToPatch (tr->size.min, tr->size.max, tr->startpos, tr->endpos, tr->trace, b); + } + } + else + { //node (x y or z) + struct bihbox_s bounds; + struct bihbox_s newbounds; + float distnear, distfar, nearfrac, farfrac, min, max; + unsigned int axis = node->type, child; + + if (!tr->totalmove[axis]) + { //doesn't move with respect to this axis. don't allow infinities. + for (child = 0; child < 2; child++) + { //only recurse if we are actually within the child + min = node->cmin[child] - tr->expand[axis]; + max = node->cmax[child] + tr->expand[axis]; + if (min <= tr->startpos[axis] && tr->startpos[axis] <= max) + { + bounds = *nodebox; + bounds.min[axis] = min; + bounds.max[axis] = max; + CM_RecursiveBIHTrace(tr, node+node->firstchild+child, movesubbounds, &bounds); + } + } + } + else if (tr->negativedir[axis]) + { //trace goes from right to left so favour the right. + for (child = 2; child-- > 0;) + { + bounds = *nodebox; + bounds.min[axis] = node->cmin[child] - tr->expand[axis]; + bounds.max[axis] = node->cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size + +// CM_RecursiveBIHTrace(tr, node+node->firstchild+child, nodebox, &bounds); +// continue; + + if (bounds.min[axis] > movesubbounds->max[axis]) + continue; //(clipped) move bounds is outside this child + if (bounds.max[axis] < movesubbounds->min[axis]) + continue; //(clipped) move bounds is outside this child + + distnear = bounds.max[axis] - tr->startpos[axis]; + nearfrac = distnear/tr->totalmove[axis]; +// if (/*nearfrac <= trace_truefraction &&*/ tr->startpos[axis] >= bounds.min[axis] && tr->trace->endpos[axis] <= bounds.max[axis]) + { + //we need to be careful when tracing, so that we can ignore children that do not overlap the trace + if (movesubbounds->min[axis] < bounds.min[axis]) + { + distfar = bounds.min[axis] - tr->startpos[axis]; + farfrac = distfar/tr->totalmove[axis]; + VectorMA(tr->startpos, farfrac, tr->totalmove, newbounds.min); //clip the new movebounds (this is more to clip the other axis too) + } + else + VectorCopy(movesubbounds->min, newbounds.min); + if (bounds.max[axis] < movesubbounds->max[axis]) + VectorMA(tr->startpos, nearfrac, tr->totalmove, newbounds.max); + else + VectorCopy(movesubbounds->max, newbounds.max); + CM_RecursiveBIHTrace(tr, node+node->firstchild+child, &newbounds, &bounds); + } + } + } + else + { //trace goes from left to right + for (child = 0; child < 2; child++) + { + bounds = *nodebox; + bounds.min[axis] = node->cmin[child] - tr->expand[axis]; + bounds.max[axis] = node->cmax[child] + tr->expand[axis]; //expand the bounds according to the player's size + +// CM_RecursiveBIHTrace(tr, node+node->firstchild+child, nodebox, &bounds); +// continue; + + if (bounds.min[axis] > movesubbounds->max[axis]) + continue; //(clipped) move bounds is outside this child + if (bounds.max[axis] < movesubbounds->min[axis]) + continue; //(clipped) move bounds is outside this child + + distnear = bounds.min[axis] - tr->startpos[axis]; + nearfrac = distnear/tr->totalmove[axis]; +// if (/*nearfrac <= trace_truefraction &&*/ tr->startpos[axis] <= bounds.max[axis] && tr->trace->endpos[axis] >= bounds.min[axis]) + { + //we need to be careful when tracing, so that we can ignore children that do not overlap the trace + if (movesubbounds->min[axis] < bounds.min[axis]) + VectorMA(tr->startpos, nearfrac, tr->totalmove, newbounds.min); //clip the new movebounds (this is more to clip the other axis too) + else + VectorCopy(movesubbounds->min, newbounds.min); + if (bounds.max[axis] < movesubbounds->max[axis]) + { + distfar = bounds.max[axis] - tr->startpos[axis]; + farfrac = distfar/tr->totalmove[axis]; + VectorMA(tr->startpos, farfrac, tr->totalmove, newbounds.max); + } + else + VectorCopy(movesubbounds->max, newbounds.max); + CM_RecursiveBIHTrace(tr, node+node->firstchild+child, &newbounds, &bounds); + } + } + } + } +} + +static void CM_RecursiveBIHTest (fte_restrict struct bihtrace_s *tr, fte_restrict const struct bihnode_s *node) +{ //with BIH, its possible for a large child node to have a box larger than its sibling. + if (node->type > BIH_Z) + { //leaf + if (node->type == BIH_BRUSH) + { + q2cbrush_t *b = node->data.brush; + if (b->contents & trace_contents) +// if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs)) + CM_TestBoxInBrush (tr->size.min, tr->size.max, tr->startpos, tr->trace, b); + } + else //if (node->type == BIH_PATCHBRUSH) + { + q2cbrush_t *b = node->data.patchbrush; + if (b->contents & trace_contents) +// if (BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs)) + CM_TestBoxInPatch (tr->size.min, tr->size.max, tr->startpos, tr->trace, b); + } + } + else + { //node (x y or z) + float min; float max; + int axis = node->type; + min = node->cmin[0] - tr->expand[axis]; + max = node->cmax[0] + tr->expand[axis]; //expand the bounds according to the player's size + + //the point can potentially be within both children, or neither. + //it doesn't really matter which order we walk the tree, just be sure to do it efficiently. + if (min <= tr->startpos[axis] && tr->startpos[axis] <= max) + { + CM_RecursiveBIHTest(tr, node+node->firstchild+0); + if (trace_trace.allsolid) + return; + } + + min = node->cmin[1] - tr->expand[axis]; + max = node->cmax[1] + tr->expand[axis]; + if (min <= tr->startpos[axis] && tr->startpos[axis] <= max) + return CM_RecursiveBIHTest(tr, node+node->firstchild+1); + } +} + +static unsigned int CM_PointContentsBIH (fte_restrict const struct bihnode_s *node, fte_restrict const vec3_t p) +{ + if (node->type > BIH_Z) + { //leaf + if (node->type == BIH_BRUSH) + { + q2cbrush_t *b = node->data.brush; + q2cbrushside_t *brushside = b->brushside; + size_t j; + if (!BoundsIntersect(p, p, b->absmins, b->absmaxs)) + return 0; + + for ( j = 0; j < b->numsides; j++, brushside++ ) + { + if ( PlaneDiff (p, brushside->plane) > 0 ) + return 0; + } + return b->contents; //inside all planes + } + else //if (node->type == BIH_PATCHBRUSH) + { //patches have no contents... + return 0; + } + } + else + { //node (x y or z) + unsigned int contents; + + //the point can potentially be within both children, or neither. + //it doesn't really matter which order we walk the tree, just be sure to do it efficiently. + if (node->cmin[0] <= p[node->type] && p[node->type] <= node->cmax[0]) + contents = CM_PointContentsBIH(node+node->firstchild+0, p); + else + contents = 0; + + if (node->cmin[1] <= p[node->type] && p[node->type] <= node->cmax[1]) + contents |= CM_PointContentsBIH(node+node->firstchild+1, p); + return contents; + } +} + +struct bihleaf_s +{ + int type; + vec3_t mins; + vec3_t maxs; + union bihdata_s data; +}; + +static int QDECL CM_SortBIH_X (const void *va, const void *vb) +{ + const struct bihleaf_s *a = va, *b = vb; + float am = a->maxs[0]+a->mins[0]; + float bm = b->maxs[0]+b->mins[0]; + if (am == bm) + return 0; + return am > bm; +} +static int QDECL CM_SortBIH_Y (const void *va, const void *vb) +{ + const struct bihleaf_s *a = va, *b = vb; + float am = a->maxs[1]+a->mins[1]; + float bm = b->maxs[1]+b->mins[1]; + if (am == bm) + return 0; + return am > bm; +} +static int QDECL CM_SortBIH_Z (const void *va, const void *vb) +{ + const struct bihleaf_s *a = va, *b = vb; + float am = a->maxs[2]+a->mins[2]; + float bm = b->maxs[2]+b->mins[2]; + if (am == bm) + return 0; + return am > bm; +} +static struct bihbox_s CM_BuildBIHNode (struct bihnode_s *node, struct bihnode_s **freenodes, struct bihleaf_s *leafs, size_t numleafs) +{ + struct bihbox_s bounds; + if (numleafs == 1) //the leaf just gives the brush pointer. + { + VectorCopy(leafs[0].mins, bounds.min); + VectorCopy(leafs[0].maxs, bounds.max); + node->type = leafs[0].type; + node->data = leafs[0].data; + } + else + { + size_t i, j; + size_t numleft = numleafs / 2; //this ends up splitting at the median point. + size_t numright = numleafs - numleft; + struct bihbox_s left, right; + vec3_t size; + struct bihnode_s *cnodes; + VectorCopy(leafs[0].mins, bounds.min); + VectorCopy(leafs[0].maxs, bounds.max); + for (i = 1; i < numleafs; i++) + { + for(j = 0; j < 3; j++) + { + if (bounds.min[j] > leafs[i].mins[j]) + bounds.min[j] = leafs[i].mins[j]; + if (bounds.max[j] < leafs[i].maxs[j]) + bounds.max[j] = leafs[i].maxs[j]; + } + } + VectorSubtract(bounds.max, bounds.min, size); + if (size[0] > size[1] && size[0] > size[2]) + { + qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_X); + node->type = BIH_X; + } + else if (size[1] > size[2]) + { + qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_Y); + node->type = BIH_Y; + } + else + { + qsort(leafs, numleafs, sizeof(*leafs), CM_SortBIH_Z); + node->type = BIH_Z; + } + + VectorCopy(bounds.min, node->temp_mins); + VectorCopy(bounds.max, node->temp_maxs); + + cnodes = *freenodes; + *freenodes += 2; + node->firstchild = cnodes - node; + left = CM_BuildBIHNode (cnodes+0, freenodes, leafs, numleft); + right = CM_BuildBIHNode (cnodes+1, freenodes, &leafs[numleft], numright); + + node->cmin[0] = left.min[node->type]; + node->cmax[0] = left.max[node->type]; + node->cmin[1] = right.min[node->type]; + node->cmax[1] = right.max[node->type]; + } + return bounds; +} +static struct bihnode_s *CM_BuildBIH (model_t *mod, cminfo_t *prv) +{ + size_t numleafs, numnodes, i, j; + struct bihnode_s *nodes, *tmpnodes; + struct bihleaf_s *leafs, *leaf; + numleafs = prv->numbrushes; + for (i = 0; i < prv->numpatches; i++) + numleafs += prv->patches[i].numfacets; + numnodes = numleafs*2-1; + leafs = BZ_Malloc(sizeof(*leafs)*numleafs); + nodes = ZG_Malloc(&mod->memgroup, sizeof(*nodes)*numnodes); + for (leaf=leafs, i = 0; i < prv->numbrushes; i++, leaf++) + { + q2cbrush_t *b = &prv->brushes[i]; + leaf->type = BIH_BRUSH; + leaf->data.brush = b; + VectorCopy(b->absmins, leaf->mins); + VectorCopy(b->absmaxs, leaf->maxs); + } + for (i = 0; i < prv->numpatches; i++) + { + q3cpatch_t *p = &prv->patches[i]; + for (j = 0; j < p->numfacets; j++, leaf++) + { + leaf->type = BIH_PATCHBRUSH; + leaf->data.patchbrush = &p->facets[j]; + VectorCopy(p->facets[j].absmins, leaf->mins); + VectorCopy(p->facets[j].absmaxs, leaf->maxs); + } + } + tmpnodes = nodes+1; + CM_BuildBIHNode(nodes, &tmpnodes, leafs, numleafs); + if (tmpnodes != nodes+numnodes) + Sys_Error("CM_BuildBIH: generated wrong number of nodes"); + BZ_Free(leafs); //just for temporary storage so that CM_BuildBIHNode doesn't need to care + return nodes; +} //====================================================================== @@ -6019,6 +6450,9 @@ static trace_t CM_BoxTrace (model_t *mod, vec3_t start, vec3_t end, trace_capsulesize[0] = ((maxs[0]-mins[0]) + (maxs[1]-mins[1]))/4.0; trace_capsulesize[1] = maxs[2]; trace_capsulesize[2] = mins[2]; + //make sure the mins_z/maxs_z isn't screwed. +// if (trace_capsulesize[1]-trace_capsulesize[2] < trace_capsulesize[0]) +// trace_capsulesize[1] = trace_capsulesize[0]+trace_capsulesize[2]; ext = (trace_capsulesize[1] > -trace_capsulesize[2])?trace_capsulesize[1]:-trace_capsulesize[2]; trace_capsulesize[1] -= trace_capsulesize[0]; trace_capsulesize[2] += trace_capsulesize[0]; @@ -6084,6 +6518,29 @@ static trace_t CM_BoxTrace (model_t *mod, vec3_t start, vec3_t end, } else #endif + if (((cminfo_t*)mod->meshinfo)->bihnodes) + { + cminfo_t *prv = mod->meshinfo; + struct bihtrace_s tr; + int j; + VectorCopy(trace_mins, tr.size.min); + VectorCopy(trace_maxs, tr.size.max); + VectorCopy(trace_absmins, tr.bounds.min); + VectorCopy(trace_absmaxs, tr.bounds.max); + for (j = 0; j < 3; j++) + tr.negativedir[j] = (end[j] - start[j]) < 0; + VectorSubtract(end, start, tr.totalmove); + VectorCopy(trace_extents, tr.expand); + + tr.startpos = trace_start; + tr.endpos = trace_end; + tr.trace = &trace_trace; + if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + CM_RecursiveBIHTest(&tr, prv->bihnodes); + else + CM_RecursiveBIHTrace(&tr, prv->bihnodes, &tr.bounds, &tr.bounds); + } + else // // check for position test special case // @@ -6846,6 +7303,7 @@ void CM_Init(void) //register cvars. Cvar_Register(&q3bsp_surf_meshcollision_flag, MAPOPTIONS); Cvar_Register(&q3bsp_surf_meshcollision_force, MAPOPTIONS); Cvar_Register(&q3bsp_mergeq3lightmaps, MAPOPTIONS); + Cvar_Register(&q3bsp_bihtraces, MAPOPTIONS); Cvar_Register(&r_subdivisions, MAPOPTIONS); CM_InitBoxHull (); diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index fdf2c7072..2e8ea7873 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -126,7 +126,7 @@ typedef struct { //void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); //void _VectorCopy (vec3_t in, vec3_t out); //void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs); float anglemod (float a); void QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); void QDECL VectorAngles (float *forward, float *up, float *angles, qboolean meshpitch); //up may be NULL diff --git a/engine/common/pmove.h b/engine/common/pmove.h index 5b5f3197e..7b82304f7 100644 --- a/engine/common/pmove.h +++ b/engine/common/pmove.h @@ -122,15 +122,15 @@ typedef struct { qboolean slidyslopes; int stepheight; - qbyte coordsize; + qbyte coordsize; //FIXME: EZPEXT1_FLOATENTCOORDS should mean 4, but the result does not match ezquake/mvdsv which would result in inconsistencies. so player coords are rounded inconsistently. unsigned int flags; } movevars_t; #define MOVEFLAG_VALID 0x80000000 //to signal that these are actually known. otherwise reserved. -#define MOVEFLAG_Q2AIRACCELERATE 0x00000001 +//#define MOVEFLAG_Q2AIRACCELERATE 0x00000001 #define MOVEFLAG_NOGRAVITYONGROUND 0x00000002 //no slope sliding -#define MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE 0x00000004 //apply half-gravity both before AND after the move, which better matches the curve +//#define MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE 0x00000004 //apply half-gravity both before AND after the move, which better matches the curve #define MOVEFLAG_QWEDGEBOX 0x00010000 //calculate edgefriction using tracebox and a buggy start pos #define MOVEFLAG_QWCOMPAT (MOVEFLAG_NOGRAVITYONGROUND|MOVEFLAG_QWEDGEBOX) diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 19845f0b5..810a7cbf7 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -730,6 +730,7 @@ typedef enum VF_RT_DESTCOLOUR7 = 219, VF_ENVMAP = 220, //cubemap image for reflectcube VF_USERDATA = 221, + VF_SKYROOM_CAMERA = 222, } viewflags; /*FIXME: this should be changed*/ @@ -777,8 +778,8 @@ enum lightfield_e lfield_dietime=14, lfield_rgbdecay=15, lfield_radiusdecay=16, - - lfield_stylestring=17 + lfield_stylestring=17, + lfield_nearclip=18 }; enum csqc_input_event { diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 498d9cacf..0ba59a4f5 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1059,7 +1059,7 @@ void GLBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float sha { extern cvar_t r_shadow_shadowmapping_bias; extern cvar_t r_shadow_shadowmapping_nearclip; - float n = r_shadow_shadowmapping_nearclip.value; + float n = dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value; float f = dl->radius; float b = r_shadow_shadowmapping_bias.value; @@ -1303,13 +1303,12 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) GL_LazyBind(tmu, GL_TEXTURE_3D, t); return; - case T_GEN_VIDEOMAP: #ifdef HAVE_MEDIA_DECODER + case T_GEN_VIDEOMAP: t = Media_UpdateForShader(pass->cin); -#else t = shaderstate.curtexnums?shaderstate.curtexnums->base:r_nulltex; -#endif break; +#endif case T_GEN_CURRENTRENDER: T_Gen_CurrentRender(tmu); @@ -4261,7 +4260,7 @@ qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned float view[16]; float proj[16]; extern cvar_t r_shadow_shadowmapping_nearclip; - Matrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, r_shadow_shadowmapping_nearclip.value, dl->radius, false); + Matrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value, dl->radius, false); Matrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[1], axis[2], dl->origin); Matrix4_Multiply(proj, view, shaderstate.lightprojmatrix); } @@ -5442,8 +5441,13 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)// || shaderstate.mode == BEM_WIREFRAME) { + float il = shaderstate.identitylighting; //this stuff sucks! if (R_DrawSkyChain(batch)) + { + shaderstate.identitylighting = il; continue; + } + shaderstate.identitylighting = il; } else if (/*shaderstate.mode != BEM_FOG &&*/ shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME) continue; diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 46f81d3cd..d3cf78bdf 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -1188,7 +1188,7 @@ qboolean R_LoadRTLights(void) float radius; vec3_t rgb; vec3_t avel; - float fov; + float fov, nearclip; unsigned int flags; float coronascale; @@ -1294,7 +1294,7 @@ qboolean R_LoadRTLights(void) file = COM_Parse(file); flags |= file?atoi(com_token):LFLAG_REALTIMEMODE; - fov = avel[0] = avel[1] = avel[2] = 0; + nearclip = fov = avel[0] = avel[1] = avel[2] = 0; *customstyle = 0; while(file) { @@ -1307,6 +1307,8 @@ qboolean R_LoadRTLights(void) avel[2] = file?atof(com_token+5):0; else if (!strncmp(com_token, "fov=", 4)) fov = file?atof(com_token+4):0; + else if (!strncmp(com_token, "nearclip=", 9)) + nearclip = file?atof(com_token+9):0; else if (!strncmp(com_token, "nostencil=", 10)) flags |= atoi(com_token+10)?LFLAG_SHADOWMAP:0; else if (!strncmp(com_token, "crepuscular=", 12)) @@ -1333,6 +1335,7 @@ qboolean R_LoadRTLights(void) dl->die = 0; dl->flags = flags; dl->fov = fov; + dl->nearclip = nearclip; dl->lightcolourscales[0] = ambientscale; dl->lightcolourscales[1] = diffusescale; dl->lightcolourscales[2] = specularscale; @@ -1418,6 +1421,8 @@ static void R_SaveRTLights_f(void) //spotlights if (light->fov) VFS_PRINTF(f, " fov=%g", light->fov); //aka: outer cone + if (light->nearclip) + VFS_PRINTF(f, " nearclip=%g", light->nearclip); //aka: distance into a wall, for lights that are meant to appear to come from a texture if (light->customstyle) VFS_PRINTF(f, " \"stylestring=%s\"", light->customstyle); //aka: outer cone @@ -1588,6 +1593,8 @@ static int R_EditLight(dlight_t *dl, const char *cmd, int argc, const char *x, c else if (!strcmp(cmd, "outercone") || !strcmp(cmd, "fov")) dl->fov = atof(x); + else if (!strcmp(cmd, "nearclip")) + dl->nearclip = atof(x); else if (!strcmp(cmd, "color") || !strcmp(cmd, "colour")) { dl->color[0] = atof(x); @@ -1676,6 +1683,7 @@ void R_EditLights_DrawInfo(void) "RealTimeMode : %s\n" " Spin : %.0f %.0f %.0f\n" " Cone : %.0f\n" + " Nearclip : %.0f\n" //"NoStencil : %s\n" //"Crepuscular : %s\n" //"Ortho : %s\n" @@ -1686,7 +1694,7 @@ void R_EditLights_DrawInfo(void) ,((dl->flags&LFLAG_NOSHADOWS)?"no":"yes"), dl->cubemapname, dl->coronascale ,dl->lightcolourscales[0], dl->lightcolourscales[1], dl->lightcolourscales[2] ,((dl->flags&LFLAG_NORMALMODE)?"yes":"no"), ((dl->flags&LFLAG_REALTIMEMODE)?"yes":"no") - ,dl->rotation[0],dl->rotation[1],dl->rotation[2], dl->fov + ,dl->rotation[0],dl->rotation[1],dl->rotation[2], dl->fov, dl->nearclip //,((dl->flags&LFLAG_SHADOWMAP)?"no":"yes"),((dl->flags&LFLAG_CREPUSCULAR)?"yes":"no"),((dl->flags&LFLAG_ORTHO)?"yes":"no") ); } @@ -1882,6 +1890,7 @@ static void R_EditLights_Edit_f(void) Con_Printf("RealTimeMode : ^[%s\\type\\r_editlights_edit realtimemode %s^]\n", ((dl->flags&LFLAG_REALTIMEMODE)?"yes":"no"), ((dl->flags&LFLAG_REALTIMEMODE)?"yes":"no")); Con_Printf("Spin : ^[%f %f %f\\type\\r_editlights_edit avel %g %g %g^]\n", dl->rotation[0],dl->rotation[1],dl->rotation[2], dl->origin[0],dl->origin[1],dl->origin[2]); Con_Printf("Cone : ^[%f\\type\\r_editlights_edit outercone %g^]\n", dl->fov, dl->fov); + Con_Printf("NearClip : ^[%f\\type\\r_editlights_edit nearclip %g^]\n", dl->nearclip, dl->nearclip); // Con_Printf("NoStencil : ^[%s\\type\\r_editlights_edit nostencil %s^]\n", ((dl->flags&LFLAG_SHADOWMAP)?"no":"yes"), ((dl->flags&LFLAG_SHADOWMAP)?"no":"yes")); // Con_Printf("Crepuscular : ^[%s\\type\\r_editlights_edit crepuscular %s^]\n", ((dl->flags&LFLAG_CREPUSCULAR)?"yes":"no"), ((dl->flags&LFLAG_CREPUSCULAR)?"yes":"no")); // Con_Printf("Ortho : ^[%s\\type\\r_editlights_edit ortho %s^]\n", ((dl->flags&LFLAG_ORTHO)?"yes":"no"), ((dl->flags&LFLAG_ORTHO)?"yes":"no")); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 3a677744c..0c68ffd2e 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -709,6 +709,11 @@ static int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, ch *name += 7; flags |= IF_CLAMP; } + else if (!Q_strnicmp(*name, "$premul:", 8)) + { //have the engine premultiply textures for you, instead of needing to do it in an editor. + *name += 8; + flags |= IF_PREMULTIPLYALPHA; + } else if (!Q_strnicmp(*name, "$3d:", 4)) { *name+=4; @@ -1120,19 +1125,10 @@ static void Shader_Portal (parsestate_t *ps, char **ptr) static void Shader_PolygonOffset (parsestate_t *ps, char **ptr) { - int m; - char *token; shader_t *shader = ps->s; + float m = Shader_ParseFloat(shader, ptr, 1); - token = Shader_ParseString(ptr); - m = atoi(token); - - if (m) { - shader->polyoffset.unit = -25 * m; - } else { - shader->polyoffset.unit = -25; - } - + shader->polyoffset.unit = -25 * m; shader->polyoffset.factor = -0.05; shader->flags |= SHADER_POLYGONOFFSET; //some backends might be lazy and only allow simple values. } @@ -5085,7 +5081,11 @@ done:; if (pass->rgbgen != RGB_GEN_IDENTITY && pass->rgbgen != RGB_GEN_IDENTITY_OVERBRIGHT && pass->rgbgen != RGB_GEN_IDENTITY_LIGHTING) weight += 100; - if (pass->texgen != T_GEN_ANIMMAP && pass->texgen != T_GEN_SINGLEMAP && pass->texgen != T_GEN_VIDEOMAP) + if (pass->texgen != T_GEN_ANIMMAP && pass->texgen != T_GEN_SINGLEMAP +#ifdef HAVE_MEDIA_DECODER + && pass->texgen != T_GEN_VIDEOMAP +#endif + ) weight += 1000; if ((pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP) && pass->anim_frames[0] && *pass->anim_frames[0]->ident == '$') @@ -7163,7 +7163,46 @@ static char *Shader_DecomposePass(char *o, shaderpass_t *p, qboolean simple) return o; } -static char *Shader_DecomposeSubPass(char *o, shaderpass_t *p, qboolean simple) +static void Shader_DecomposeSubPassMap(char *o, shader_t *s, char *name, texid_t tex) +{ + if (tex) + { + unsigned int flags = tex->flags; + sprintf(o, "%s \"%s\" %ix%i%s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", name, tex->ident, tex->width, tex->height, + (tex->status == TEX_FAILED)?" FAILED":"", + Image_FormatName(tex->format), + (flags&IF_CLAMP)?" clamp":"", + (flags&IF_NOMIPMAP)?" nomipmap":"", + (flags&IF_NEAREST)?" nearest":"", + (flags&IF_LINEAR)?" linear":"", + (flags&IF_UIPIC)?" uipic":"", + (flags&IF_SRGB)?" srgb":"", + + (flags&IF_NOPICMIP)?" nopicmip":"", + (flags&IF_NOALPHA)?" noalpha":"", + (flags&IF_NOGAMMA)?" noalpha":"", + (flags&IF_TEXTYPE)?" non-2d":"", + (flags&IF_MIPCAP)?"":" nomipcap", + (flags&IF_PREMULTIPLYALPHA)?" premultiply":"", + + (flags&IF_NOSRGB)?" nosrgb":"", + + (flags&IF_PALETTIZE)?" palettize":"", + (flags&IF_NOPURGE)?" nopurge":"", + (flags&IF_HIGHPRIORITY)?" highpri":"", + (flags&IF_LOWPRIORITY)?" lowpri":"", + (flags&IF_LOADNOW)?" loadnow":"", + (flags&IF_TRYBUMP)?" trybump":"", + (flags&IF_RENDERTARGET)?" rendertarget":"", + (flags&IF_EXACTEXTENSION)?" exactext":"", + (flags&IF_NOREPLACE)?" noreplace":"", + (flags&IF_NOWORKER)?" noworker":"" + ); + } + else + sprintf(o, "%s (%s)", name, "UNDEFINED"); +} +static char *Shader_DecomposeSubPass(char *o, shader_t *s, shaderpass_t *p, qboolean simple) { int i; if (!simple) @@ -7224,70 +7263,36 @@ static char *Shader_DecomposeSubPass(char *o, shaderpass_t *p, qboolean simple) switch(p->texgen) { default: sprintf(o, "T_GEN_? "); break; - case T_GEN_SINGLEMAP: - if (p->anim_frames[0]) - { - unsigned int flags = p->anim_frames[0]->flags; - sprintf(o, "singlemap \"%s\" %ix%i%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", p->anim_frames[0]->ident, p->anim_frames[0]->width, p->anim_frames[0]->height, - (p->anim_frames[0]->status == TEX_FAILED)?" FAILED":"", - (flags&IF_CLAMP)?" clamp":"", - (flags&IF_NOMIPMAP)?" nomipmap":"", - (flags&IF_NEAREST)?" nearest":"", - (flags&IF_LINEAR)?" linear":"", - (flags&IF_UIPIC)?" uipic":"", - (flags&IF_SRGB)?" srgb":"", - - (flags&IF_NOPICMIP)?" nopicmip":"", - (flags&IF_NOALPHA)?" noalpha":"", - (flags&IF_NOGAMMA)?" noalpha":"", - (flags&IF_TEXTYPE)?" non-2d":"", - (flags&IF_MIPCAP)?"":" nomipcap", - (flags&IF_PREMULTIPLYALPHA)?" premultiply":"", - - (flags&IF_NOSRGB)?" nosrgb":"", - - (flags&IF_PALETTIZE)?" palettize":"", - (flags&IF_NOPURGE)?" nopurge":"", - (flags&IF_HIGHPRIORITY)?" highpri":"", - (flags&IF_LOWPRIORITY)?" lowpri":"", - (flags&IF_LOADNOW)?" loadnow":"", - (flags&IF_TRYBUMP)?" trybump":"", - (flags&IF_RENDERTARGET)?" rendertarget":"", - (flags&IF_EXACTEXTENSION)?" exactext":"", - (flags&IF_NOREPLACE)?" noreplace":"", - (flags&IF_NOWORKER)?" noworker":"" - ); - } - else - sprintf(o, "singlemap "); - break; - case T_GEN_ANIMMAP: sprintf(o, "animmap "); break; - case T_GEN_LIGHTMAP: sprintf(o, "lightmap "); break; - case T_GEN_DELUXMAP: sprintf(o, "deluxmap "); break; - case T_GEN_SHADOWMAP: sprintf(o, "shadowmap "); break; - case T_GEN_LIGHTCUBEMAP: sprintf(o, "lightcubemap "); break; - case T_GEN_DIFFUSE: sprintf(o, "diffuse "); break; - case T_GEN_NORMALMAP: sprintf(o, "normalmap "); break; - case T_GEN_SPECULAR: sprintf(o, "specular "); break; - case T_GEN_UPPEROVERLAY: sprintf(o, "upperoverlay "); break; - case T_GEN_LOWEROVERLAY: sprintf(o, "loweroverlay "); break; - case T_GEN_FULLBRIGHT: sprintf(o, "fullbright "); break; - case T_GEN_PALETTED: sprintf(o, "paletted "); break; - case T_GEN_REFLECTCUBE: sprintf(o, "reflectcube "); break; - case T_GEN_REFLECTMASK: sprintf(o, "reflectmask "); break; - case T_GEN_DISPLACEMENT: sprintf(o, "displacementmap "); break; - case T_GEN_CURRENTRENDER: sprintf(o, "currentrender "); break; - case T_GEN_SOURCECOLOUR: sprintf(o, "sourcecolour "); break; - case T_GEN_SOURCEDEPTH: sprintf(o, "sourcedepth "); break; - case T_GEN_REFLECTION: sprintf(o, "reflection "); break; - case T_GEN_REFRACTION: sprintf(o, "refraction "); break; - case T_GEN_REFRACTIONDEPTH: sprintf(o, "refractiondepth "); break; - case T_GEN_RIPPLEMAP: sprintf(o, "ripplemap "); break; - case T_GEN_SOURCECUBE: sprintf(o, "sourcecube "); break; - case T_GEN_VIDEOMAP: sprintf(o, "videomap "); break; - case T_GEN_CUBEMAP: sprintf(o, "cubemap "); break; - case T_GEN_3DMAP: sprintf(o, "3dmap "); break; - case T_GEN_GBUFFERCASE: sprintf(o, "gbuffer%i ",p->texgen-T_GEN_GBUFFER0); break; + case T_GEN_SINGLEMAP: Shader_DecomposeSubPassMap(o, s, "singlemap", p->anim_frames[0]); break; + case T_GEN_ANIMMAP: Shader_DecomposeSubPassMap(o, s, "animmap", p->anim_frames[0]); break; +#ifdef HAVE_MEDIA_DECODER + case T_GEN_VIDEOMAP: Shader_DecomposeSubPassMap(o, s, "videomap", Media_UpdateForShader(p->cin)); break; +#endif + case T_GEN_CUBEMAP: Shader_DecomposeSubPassMap(o, s, "cubemap", p->anim_frames[0]); break; + case T_GEN_3DMAP: Shader_DecomposeSubPassMap(o, s, "3dmap", p->anim_frames[0]); break; + case T_GEN_LIGHTMAP: sprintf(o, "lightmap "); break; + case T_GEN_DELUXMAP: sprintf(o, "deluxemap "); break; + case T_GEN_SHADOWMAP: sprintf(o, "shadowmap "); break; + case T_GEN_LIGHTCUBEMAP: sprintf(o, "lightcubemap "); break; + case T_GEN_DIFFUSE: Shader_DecomposeSubPassMap(o, s, "diffuse", s->defaulttextures[0].base); break; + case T_GEN_NORMALMAP: Shader_DecomposeSubPassMap(o, s, "normalmap", s->defaulttextures[0].bump); break; + case T_GEN_SPECULAR: Shader_DecomposeSubPassMap(o, s, "specular", s->defaulttextures[0].specular); break; + case T_GEN_UPPEROVERLAY: Shader_DecomposeSubPassMap(o, s, "upper", s->defaulttextures[0].upperoverlay); break; + case T_GEN_LOWEROVERLAY: Shader_DecomposeSubPassMap(o, s, "lower", s->defaulttextures[0].loweroverlay); break; + case T_GEN_FULLBRIGHT: Shader_DecomposeSubPassMap(o, s, "fullbright", s->defaulttextures[0].fullbright); break; + case T_GEN_PALETTED: Shader_DecomposeSubPassMap(o, s, "paletted", s->defaulttextures[0].paletted); break; + case T_GEN_REFLECTCUBE: Shader_DecomposeSubPassMap(o, s, "reflectcube", s->defaulttextures[0].reflectcube); break; + case T_GEN_REFLECTMASK: Shader_DecomposeSubPassMap(o, s, "reflectmask", s->defaulttextures[0].reflectmask); break; + case T_GEN_DISPLACEMENT: Shader_DecomposeSubPassMap(o, s, "displacement", s->defaulttextures[0].displacement); break; + case T_GEN_CURRENTRENDER: sprintf(o, "currentrender "); break; + case T_GEN_SOURCECOLOUR: sprintf(o, "sourcecolour"); break; + case T_GEN_SOURCEDEPTH: sprintf(o, "sourcedepth"); break; + case T_GEN_REFLECTION: sprintf(o, "reflection"); break; + case T_GEN_REFRACTION: sprintf(o, "refraction"); break; + case T_GEN_REFRACTIONDEPTH: sprintf(o, "refractiondepth"); break; + case T_GEN_RIPPLEMAP: sprintf(o, "ripplemap"); break; + case T_GEN_SOURCECUBE: sprintf(o, "sourcecube"); break; + case T_GEN_GBUFFERCASE: sprintf(o, "gbuffer%i ",p->texgen-T_GEN_GBUFFER0); break; } o+=strlen(o); @@ -7330,7 +7335,7 @@ char *Shader_Decompose(shader_t *s) p = s->passes; o = Shader_DecomposePass(o, p, true); for (j = 0; j < s->numpasses; j++) - o = Shader_DecomposeSubPass(o, p+j, true); + o = Shader_DecomposeSubPass(o, s, p+j, true); } else { @@ -7341,7 +7346,7 @@ char *Shader_Decompose(shader_t *s) o = Shader_DecomposePass(o, p, false); for (j = 0; j < p->numMergedPasses; j++) - o = Shader_DecomposeSubPass(o, p+j, !!p->prog); + o = Shader_DecomposeSubPass(o, s, p+j, !!p->prog); sprintf(o, "}\n"); o+=strlen(o); } } diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 92d290faa..57c3626d2 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -2223,7 +2223,7 @@ static void Sh_LightFrustumPlanes(dlight_t *l, vec3_t axis[3], vec4_t *planes, i VectorCopy(l->origin, planes[4]); VectorScale(axis[axis0], dir, planes[4]); VectorNormalize(planes[4]); - planes[4][3] = r_shadow_shadowmapping_nearclip.value + DotProduct(planes[4], l->origin); + planes[4][3] = (l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value) + DotProduct(planes[4], l->origin); for (i = 0; i < 4; i++) { @@ -2497,7 +2497,7 @@ qboolean Sh_GenShadowMap (dlight_t *l, int lighttype, vec3_t axis[3], qbyte *lvi smesh = SHM_BuildShadowMesh(l, lvis, (lighttype & LSHADER_ORTHO)?SMT_ORTHO:SMT_SHADOWMAP); if (lighttype & LSHADER_SPOT) - Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, l->fov, l->fov, r_shadow_shadowmapping_nearclip.value, l->radius, false); + Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, l->fov, l->fov, l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value, l->radius, false); else if (lighttype & LSHADER_ORTHO) { float xmin = -l->radius; @@ -2509,7 +2509,7 @@ qboolean Sh_GenShadowMap (dlight_t *l, int lighttype, vec3_t axis[3], qbyte *lvi Matrix4x4_CM_Orthographic(r_refdef.m_projection_std, xmin, xmax, ymax, ymin, znear, zfar); } else - Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, 90, 90, r_shadow_shadowmapping_nearclip.value, l->radius, false); + Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, 90, 90, l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value, l->radius, false); memcpy(r_refdef.m_projection_view, r_refdef.m_projection_std, sizeof(r_refdef.m_projection_view)); diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index e90da6f38..a69b87840 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -115,6 +115,74 @@ void R_DrawFastSky(batch_t *batch) b.texture = NULL; BE_SubmitBatch(&b); } + +void R_RenderScene (void); +qboolean R_DrawSkyroom(shader_t *skyshader) +{ +#ifdef GLQUAKE + float vmat[16]; + refdef_t oldrefdef; + + if (qrenderer != QR_OPENGL) + return false; //FIXME + if (r_viewcluster == -1) + return false; //don't draw the skyroom if the camera is outside. + + if (!r_refdef.skyroom_enabled || r_refdef.recurse >= R_MAX_RECURSE-1) + return false; + + if (skyshader->numpasses) + { + shaderpass_t *pass = skyshader->passes; + if (pass->shaderbits & SBITS_ATEST_BITS) //alphatests + ; + else if (pass->shaderbits & SBITS_MASK_BITS) //colormasks + ; + else if ((pass->shaderbits & SBITS_BLEND_BITS) != 0 && (pass->shaderbits & SBITS_BLEND_BITS) != (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO)) //blendfunc + ; + else + return false; //that shader looks like its opaque. + } + + oldrefdef = r_refdef; + r_refdef.recurse+=1; + + r_refdef.externalview = true; + r_refdef.skyroom_enabled = false; + + /*work out where the camera should be (use the same angles)*/ + VectorCopy(r_refdef.skyroom_pos, r_refdef.vieworg); + VectorCopy(r_refdef.skyroom_pos, r_refdef.pvsorigin); + Matrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg); + R_SetFrustum (r_refdef.m_projection_std, vmat); + + //now determine the stuff the backend will use. + memcpy(r_refdef.m_view, vmat, sizeof(float)*16); + VectorAngles(vpn, vup, r_refdef.viewangles, false); + VectorCopy(r_refdef.vieworg, r_origin); + + Surf_SetupFrame(); + //FIXME: just call Surf_DrawWorld instead? + R_RenderScene(); + + r_refdef = oldrefdef; + + /*broken stuff*/ + AngleVectors (r_refdef.viewangles, vpn, vright, vup); + VectorCopy (r_refdef.vieworg, r_origin); + + GLBE_SelectEntity(&r_worldentity); + GL_ForceDepthWritable(); + qglClear(GL_DEPTH_BUFFER_BIT); + + currententity = NULL; + + return true; +#else + return false; +#endif +} + /* ================= GL_DrawSkyChain @@ -150,7 +218,11 @@ qboolean R_DrawSkyChain (batch_t *batch) { skyshader = batch->shader; if (skyshader->prog) //glsl is expected to do the whole skybox/warpsky thing itself, with no assistance from this legacy code. - return false; + { + //if the first pass is transparent in some form, then be prepared to give it a skyroom behind. + R_DrawSkyroom(skyshader); + return false; //draw as normal... + } } if (skyshader->skydome) @@ -158,7 +230,12 @@ qboolean R_DrawSkyChain (batch_t *batch) else skyboxtex = NULL; - if (skyboxtex && TEXVALID(*skyboxtex)) + if (R_DrawSkyroom(skyshader)) + { + if (skyshader->numpasses) + GL_DrawSkySphere(batch, skyshader); + } + else if (skyboxtex && TEXVALID(*skyboxtex)) { //draw a skybox if we were given the textures R_CalcSkyChainBounds(batch); GL_DrawSkyBox (skyboxtex, batch); diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 88f5e6b73..71c811dcb 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -28,8 +28,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #pragma warning(disable : 4051) // ALPHA #endif -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); -qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2); +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs); +qboolean BoundsIntersect (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2); void ClearBounds (vec3_t mins, vec3_t maxs); struct builddata_s diff --git a/engine/gl/shader.h b/engine/gl/shader.h index cb0e1d437..279e8af8a 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -284,7 +284,9 @@ typedef struct shaderpass_s { T_GEN_SOURCECUBE, //used for render-to-texture targets +#ifdef HAVE_MEDIA_DECODER T_GEN_VIDEOMAP, //use the media playback as an image source, updating each frame for which it is visible +#endif T_GEN_CUBEMAP, //use a cubemap instead, otherwise like T_GEN_SINGLEMAP T_GEN_3DMAP, //use a 3d texture instead, otherwise T_GEN_SINGLEMAP. diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 46720c3f3..85b1a2bfe 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -12526,6 +12526,7 @@ void PR_DumpPlatform_f(void) {"VF_RT_RIPPLE", "const float", CS|MENU, D("The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy."), VF_RT_RIPPLE}, {"VF_ENVMAP", "const float", CS|MENU, D("The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything."), VF_ENVMAP}, {"VF_USERDATA", "const float", CS|MENU, D("Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform."), VF_USERDATA}, + {"VF_SKYROOM_CAMERA", "const float", CS, D("Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes)."), VF_SKYROOM_CAMERA}, {"RF_VIEWMODEL", "const float", CS, D("Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob."), CSQCRF_VIEWMODEL}, {"RF_EXTERNALMODEL", "const float", CS, D("Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible."), CSQCRF_EXTERNALMODEL}, @@ -12579,6 +12580,7 @@ void PR_DumpPlatform_f(void) {"LFIELD_RGBDECAY", "const float", CS, NULL, lfield_rgbdecay}, {"LFIELD_RADIUSDECAY", "const float", CS, NULL, lfield_radiusdecay}, {"LFIELD_STYLESTRING", "const float", CS, NULL, lfield_stylestring}, + {"LFIELD_NEARCLIP", "const float", CS, NULL, lfield_nearclip}, {"LFLAG_NORMALMODE", "const float", CS, NULL, LFLAG_NORMALMODE}, {"LFLAG_REALTIMEMODE", "const float", CS, NULL, LFLAG_REALTIMEMODE}, diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 68c31e3d4..78012d901 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -41,7 +41,7 @@ cvar_t sv_demoMaxDirCount = CVARD("sv_demoMaxDirCount", "500", "Maximum allowed cvar_t sv_demoMaxDirAge = CVARD("sv_demoMaxDirAge", "0", "Maximum allowed age for demos, any older demos will be deleted when sv_demoClearOld is set (this doesn't prevent recording new demos)."); cvar_t sv_demoClearOld = CVARD("sv_demoClearOld", "0", "Automatically delete demos to keep the demos count reasonable."); cvar_t sv_demoDir = CVARC("sv_demoDir", "demos", SV_DemoDir_Callback); -cvar_t sv_demoDirAlt = CVARD("sv_demoDir", "", "Provides a fallback directory name for demo downloads, for when sv_demoDir doesn't contain the requested demo."); +cvar_t sv_demoDirAlt = CVARCD("sv_demoDirAlt", "", SV_DemoDir_Callback, "Provides a fallback directory name for demo downloads, for when sv_demoDir doesn't contain the requested demo."); cvar_t sv_demofps = CVAR("sv_demofps", "30"); cvar_t sv_demoPings = CVARD("sv_demoPings", "10", "Interval between ping updates in mvds"); cvar_t sv_demoMaxSize = CVARD("sv_demoMaxSize", "", "Demos will be truncated to be no larger than this size."); @@ -780,14 +780,14 @@ static void QDECL SV_DemoDir_Callback(struct cvar_s *var, char *oldvalue) value = var->string; if (!value[0] || value[0] == '/' || (value[0] == '\\' && value[1] == '\\')) { - Cvar_ForceSet(&sv_demoDir, "demos"); + Cvar_ForceSet(var, var->enginevalue); return; } if (value[0] == '.' && value[1] == '.') value += 2; if (strstr(value,"..")) { - Cvar_ForceSet(&sv_demoDir, "demos"); + Cvar_ForceSet(var, var->enginevalue); return; } } diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index 5adcecc3d..03e4977ab 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -624,7 +624,7 @@ void Sys_Shutdown (void) { } -#ifdef __linux__ /*should probably be GNUC but whatever*/ +#if defined(__linux__) && defined(__GNUC__) #include #ifdef __i386__ #include @@ -912,7 +912,7 @@ int main(int argc, char *argv[]) -#ifdef __linux__ +#if defined(__linux__) && defined(__GNUC__) if (!COM_CheckParm("-nodumpstack")) { struct sigaction act; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 0d1f3bc7e..718bd1cf1 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -6249,6 +6249,7 @@ ucmd_t nqucmds[] = {"name", SVNQ_NQInfo_f}, {"color", SVNQ_NQColour_f}, {"playermodel", SVNQ_DPModel_f}, +// {"pmodel", SVNQ_DPModel_f}, //nehahra {"playerskin", SVNQ_DPSkin_f}, {"rate", SV_Rate_f}, {"rate_burstsize", NULL}, diff --git a/engine/shaders/vulkan/defaultwall.glsl b/engine/shaders/vulkan/defaultwall.glsl index 28e63b817..34973d304 100644 --- a/engine/shaders/vulkan/defaultwall.glsl +++ b/engine/shaders/vulkan/defaultwall.glsl @@ -183,7 +183,7 @@ void main () //add on the fullbright if (FULLBRIGHT) gl_FragColor.rgb += texture2D(s_fullbright, tc).rgb; -} + } //entity modifiers gl_FragColor = gl_FragColor * e_colourident; diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 8af6cc757..b58450e75 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -1705,12 +1705,12 @@ static texid_t SelectPassTexture(const shaderpass_t *pass) case T_GEN_CURRENTRENDER: return shaderstate.tex_currentrender; - case T_GEN_VIDEOMAP: #ifdef HAVE_MEDIA_DECODER + case T_GEN_VIDEOMAP: if (pass->cin) return Media_UpdateForShader(pass->cin); -#endif return r_nulltex; +#endif case T_GEN_LIGHTCUBEMAP: //light's projected cubemap if (shaderstate.curdlight) @@ -4268,7 +4268,7 @@ void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour) float view[16]; float proj[16]; extern cvar_t r_shadow_shadowmapping_nearclip; - Matrix4x4_CM_Projection_Far(proj, l->fov, l->fov, r_shadow_shadowmapping_nearclip.value, l->radius, false); + Matrix4x4_CM_Projection_Far(proj, l->fov, l->fov, l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value, l->radius, false); Matrix4x4_CM_ModelViewMatrixFromAxis(view, l->axis[0], l->axis[1], l->axis[2], l->origin); Matrix4_Multiply(proj, view, cbl->l_cubematrix); } @@ -6166,7 +6166,7 @@ void VKBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float sha { #define SHADOWMAP_SIZE 512 extern cvar_t r_shadow_shadowmapping_nearclip, r_shadow_shadowmapping_bias; - float nc = r_shadow_shadowmapping_nearclip.value; + float nc = dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value; float bias = r_shadow_shadowmapping_bias.value; //much of the projection matrix cancels out due to symmetry and stuff diff --git a/plugins/bullet/bulletplug.cpp b/plugins/bullet/bulletplug.cpp index 2979dcbfb..3615a3c05 100644 --- a/plugins/bullet/bulletplug.cpp +++ b/plugins/bullet/bulletplug.cpp @@ -86,7 +86,7 @@ void World_Bullet_Init(void) { physics_bullet_enable = pCvar_GetNVFDG("physics_bullet_enable", "1", 0, "", "Bullet"); physics_bullet_maxiterationsperframe = pCvar_GetNVFDG("physics_bullet_maxiterationsperframe", "10", 0, "FIXME: should be 1 when CCD is working properly.", "Bullet"); - physics_bullet_framerate = pCvar_GetNVFDG("physics_bullet_framerate", "60", 0, "", "Bullet"); + physics_bullet_framerate = pCvar_GetNVFDG("physics_bullet_framerate", "60", 0, "Bullet physics run at a fixed framerate in order to preserve numerical stability (interpolation is used to smooth out the result). Higher framerates are of course more demanding.", "Bullet"); pr_meshpitch = pCvar_GetNVFDG("r_meshpitch", "-1", 0, "", "Bullet"); }