From 517282334158b7aea7d34d351574f38207ff4ec9 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 11 Oct 2015 11:34:58 +0000 Subject: [PATCH] code to use occlusion queries for coronas. tweaked cl_maxfps a little. now sticks much closer to the desired rate. also tweaked cl_netfps. clamps 77fps to 13ms frames (read: 76.9 fps) in an attempt to avoid tripping up any framerate checks (we had previously been using a lower net rate with occasional 14ms frames). viewspace particles are now a thing. greater control over spawning particles. its now possible to spawn particles only in slime, etc. fix soundlength builtin. preliminary version of r_dynamic -1, which can give some significant framerate boosts on certain maps. fix halflife bsp texture issues. rewrote worker thread logic. workers now work as a single pool, instead of independent pools. this means that you don't have to wait for a specific thread to finish before it can load new stuff, reducing overall load times some more. worker_count cvar allows reconfiguring the number of active workers. can be changed any time. updated mod_terrain_create command. should be more useful now and make more sense when first loaded. fix lit support. apparently its been broken for a while. brush editor now has csg subtraction. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4990 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 26 +- engine/client/cl_input.c | 96 ++-- engine/client/cl_main.c | 7 +- engine/client/cl_parse.c | 10 +- engine/client/cl_pred.c | 1 - engine/client/cl_tent.c | 44 +- engine/client/clhl_game.c | 55 +- engine/client/client.h | 4 +- engine/client/console.c | 18 + engine/client/image.c | 63 ++- engine/client/in_generic.c | 5 +- engine/client/m_mp3.c | 12 +- engine/client/merged.h | 3 +- engine/client/p_script.c | 106 +++- engine/client/pr_clcmd.c | 14 +- engine/client/pr_csqc.c | 3 + engine/client/pr_menu.c | 11 +- engine/client/r_surf.c | 780 ++++++++++++++++++++++++--- engine/client/render.h | 4 +- engine/client/renderer.c | 10 +- engine/client/sbar.c | 2 +- engine/client/snd_xaudio.c | 256 +++++++++ engine/client/vid_headless.c | 10 +- engine/client/wad.c | 101 ++-- engine/client/wad.h | 1 + engine/client/zqtp.c | 2 +- engine/common/bothdefs.h | 2 +- engine/common/cmd.c | 2 +- engine/common/com_mesh.c | 40 +- engine/common/common.c | 454 +++++++++------- engine/common/common.h | 16 +- engine/common/fs.c | 82 +-- engine/common/plugin.c | 7 +- engine/common/pr_bgcmd.c | 2 +- engine/common/protocol.h | 3 +- engine/common/q3common.c | 2 +- engine/common/sys_win_threads.c | 27 +- engine/common/world.h | 1 + engine/gl/gl_alias.c | 2 +- engine/gl/gl_backend.c | 51 +- engine/gl/gl_font.c | 2 +- engine/gl/gl_heightmap.c | 130 ++++- engine/gl/gl_hlmdl.c | 203 +++---- engine/gl/gl_model.c | 106 ++-- engine/gl/gl_model.h | 7 +- engine/gl/gl_rlight.c | 103 +++- engine/gl/gl_rsurf.c | 6 +- engine/gl/gl_screen.c | 4 +- engine/gl/gl_shader.c | 7 +- engine/gl/gl_shadow.c | 4 +- engine/gl/gl_vidcommon.c | 40 +- engine/gl/gl_warp.c | 2 +- engine/gl/glquake.h | 9 + engine/gl/glsupp.h | 10 + engine/gl/ltface.c | 2 - engine/gl/model_hl.h | 37 +- engine/gl/shader.h | 10 +- engine/http/httpserver.c | 3 +- engine/qclib/pr_edict.c | 16 +- engine/server/pr_cmds.c | 13 +- engine/server/savegame.c | 4 +- engine/server/sv_cluster.c | 19 +- engine/server/sv_ents.c | 6 +- engine/server/sv_init.c | 4 +- engine/server/sv_main.c | 5 +- engine/server/sv_phys.c | 1 + engine/server/sv_send.c | 5 + engine/server/sv_user.c | 28 +- engine/server/svhl_game.c | 542 +++++++++++++++++-- engine/server/svhl_gcapi.h | 4 +- engine/server/svhl_phys.c | 19 +- engine/server/svhl_world.c | 10 +- engine/server/world.c | 13 +- engine/sw/sw_backend.c | 1 - quakec/csaddon/src/editor_brushes.qc | 123 +++-- specs/particles.txt | 8 + 76 files changed, 2964 insertions(+), 877 deletions(-) create mode 100644 engine/client/snd_xaudio.c diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 2e53901b7..d2256d68d 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "particles.h" #include "shader.h" +#include "glquake.h" extern cvar_t cl_predict_players; extern cvar_t cl_predict_players_frac; @@ -100,8 +101,13 @@ void CL_FreeDlights(void) int i; if (cl_dlights) for (i = 0; i < rtlights_max; i++) + { if (cl_dlights[i].worldshadowmesh) SH_FreeShadowMesh(cl_dlights[i].worldshadowmesh); + + if (cl_dlights[i].coronaocclusionquery) + qglDeleteQueriesARB(1, &cl_dlights[i].coronaocclusionquery); + } #endif rtlights_max = cl_maxdlights = 0; @@ -110,6 +116,7 @@ void CL_FreeDlights(void) } void CL_InitDlights(void) { + CL_FreeDlights(); rtlights_max = cl_maxdlights = RTL_FIRST; cl_dlights = BZ_Realloc(cl_dlights, sizeof(*cl_dlights)*cl_maxdlights); memset(cl_dlights, 0, sizeof(*cl_dlights)*cl_maxdlights); @@ -117,9 +124,12 @@ void CL_InitDlights(void) static void CL_ClearDlight(dlight_t *dl, int key) { - void *sm; - sm = dl->worldshadowmesh; + void *sm = dl->worldshadowmesh; + unsigned int oq = dl->coronaocclusionquery; + unsigned int oqr = (dl->key == key)?dl->coronaocclusionresult:false; memset (dl, 0, sizeof(*dl)); + dl->coronaocclusionquery = oq; + dl->coronaocclusionresult = oqr; dl->rebuildcache = true; dl->worldshadowmesh = sm; dl->axis[0][0] = 1; @@ -222,12 +232,6 @@ dlight_t *CL_NewDlight (int key, const vec3_t org, float radius, float time, return dl; } -dlight_t *CL_NewDlightRGB(int key, const vec3_t org, float radius, float time, - float r, float g, float b) -{ - return CL_NewDlight(key, org, radius, time, r*5, g*5, b*5); -} - /* =============== @@ -1911,7 +1915,7 @@ void VQ2_AddLerpEntity(entity_t *in) //a convienience function */ int V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b) { - return CL_NewDlightRGB (entsource, org, quant, -0.1, r, g, b) - cl_dlights; + return CL_NewDlight (entsource, org, quant, -0.1, r*5, g*5, b*5) - cl_dlights; } void CLQ1_AddOrientedHalfSphere(shader_t *shader, float radius, float gap, float *matrix, float r, float g, float b, float a) @@ -2290,7 +2294,7 @@ void CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qbo { // int oldents = cl_numvisedicts; // cl_numvisedicts = 0; - BE_DrawWorld(false, NULL); + BE_DrawWorld(NULL, NULL); cl_numstris = 0; // cl_numvisedicts = oldents; } @@ -2938,7 +2942,7 @@ void CL_LinkStaticEntities(void *pvs) model_t *clmodel; extern cvar_t r_drawflame, gl_part_flame; - if (r_drawflame.ival < 0) + if (r_drawflame.ival < 0 || r_drawentities.ival == 0) return; if (!cl.worldmodel) diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index a194cce2e..be4ea2c7a 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -31,6 +31,7 @@ static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue); cvar_t cl_nodelta = CVAR("cl_nodelta","0"); +cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0"); cvar_t cl_c2spps = CVAR("cl_c2spps", "0"); cvar_t cl_c2sImpulseBackup = SCVAR("cl_c2sImpulseBackup","3"); cvar_t cl_netfps = CVAR("cl_netfps", "150"); @@ -1177,10 +1178,16 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now re fps = bound (6.7, wantfps, fpscap); //we actually cap ourselves to 150msecs (1000/7 = 142) } + //its not time yet if (time < ceil(1000 / fps)) return 0; - return time - ceil(1000 / fps); + //clamp it if we have over 1.5 frame banked somehow + if (time - (1000 / fps) > (1000 / fps)*1.5) + return (1000 / fps) * 1.5; + + //report how much spare time the caller now has + return time - (1000 / fps); } qboolean allowindepphys; @@ -1320,10 +1327,6 @@ int CL_IndepPhysicsThread(void *param) spare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, false); if (spare) { - //don't let them bank too much and get sudden bursts - if (spare > 15) - spare = 15; - time -= spare/1000.0f; Sys_LockMutex(indeplock); if (cls.state) @@ -1529,7 +1532,8 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend) cmd->lightlevel = 0; #ifdef CSQC_DAT - CSQC_Input_Frame(plnum, cmd); + if (!runningindepphys) + CSQC_Input_Frame(plnum, cmd); #endif memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum])); } @@ -1631,8 +1635,10 @@ void CL_SendCmd (double frametime, qboolean mainloop) static float pps_balance = 0; static int dropcount = 0; static double msecs; + static double msecsround; int msecstouse; qboolean dontdrop=false; + float usetime; clcmdbuf_t *next; @@ -1739,39 +1745,28 @@ void CL_SendCmd (double frametime, qboolean mainloop) #ifdef IRCCONNECT if (cls.netchan.remote_address.type != NA_IRC) #endif - if (msecs>150) //q2 has 200 slop. - msecs=150; msecs += frametime*1000; // Con_Printf("%f\n", msecs); - if (msecs<0) - msecs=0; //erm. - - msecstouse = (int)msecs; //casts round down. - if (msecstouse == 0) - return; #ifdef IRCCONNECT if (cls.netchan.remote_address.type != NA_IRC) #endif - if (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times - msecstouse = 200; - - // align msecstouse to avoid servers wasting our msecs - if (msecstouse > 100) - msecstouse &= ~3; // align to 4 - else if (msecstouse > 50) - msecstouse &= ~1; // align to 2 wantfps = cl_netfps.value; fullsend = true; + msecstouse = 0; + #ifndef CLIENTONLY if (sv.state && cls.state != ca_active) + { fullsend = -1; + msecstouse = usetime = msecs; + msecs = 0; + } else #endif - if (!runningindepphys) { // while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up if (cls.state < ca_active && !cls.download) @@ -1783,23 +1778,42 @@ void CL_SendCmd (double frametime, qboolean mainloop) #endif wantfps = 12.5; } - if (cl_netfps.value > 0 || !fullsend) + if (!runningindepphys && (cl_netfps.value > 0 || !fullsend)) { float spare; - spare = CL_FilterTime(msecstouse, wantfps, false); - if (!spare && (msecstouse < 200 -#ifdef IRCCONNECT - || cls.netchan.remote_address.type == NA_IRC -#endif - )) + spare = CL_FilterTime(msecs, wantfps, false); + usetime = msecsround + (msecs - spare); + msecstouse = (int)usetime; + if (!spare) fullsend = false; - if (spare > cl_sparemsec.ival) - spare = cl_sparemsec.ival; - if (spare > 0) - msecstouse -= spare; + else + { + msecsround = usetime - msecstouse; + msecs = spare + msecstouse; + } + } + else + { + usetime = msecsround + msecs; + msecstouse = (int)usetime; + msecsround = usetime - msecstouse; } } + if (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times + msecstouse = 200; + + // align msecstouse to avoid servers wasting our msecs + if (msecstouse > 100) + msecstouse &= ~3; // align to 4 + else if (msecstouse > 50) + msecstouse &= ~1; // align to 2 + + if (msecstouse < 0) //FIXME + fullsend = false; + if (usetime <= 0) + return; //infinite frame times = weirdness. + #ifdef HLCLIENT if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0])) #endif @@ -1829,7 +1843,12 @@ void CL_SendCmd (double frametime, qboolean mainloop) // if (cl.spectator) Cam_Track(&cl.playerview[plnum], &independantphysics[plnum]); Cam_FinishMove(&cl.playerview[plnum], &independantphysics[plnum]); - independantphysics[plnum].msec = msecstouse; + independantphysics[plnum].msec = usetime; + + //HACK: 1000/77 = 12.98. nudge it just under so we never appear to be using 83fps at 77fps (which can trip cheat detection in mods that expect 72 fps when many servers are configured for 77) + //so lets just never use 12. + if (fullsend && (independantphysics[plnum].msec > 12.9 && independantphysics[plnum].msec < 13) && cls.maxfps == 77) + independantphysics[plnum].msec = 13; } //the main loop isn't allowed to send @@ -1839,9 +1858,12 @@ void CL_SendCmd (double frametime, qboolean mainloop) // if (skipcmd) // return; - if (!fullsend || !msecstouse) + if (!fullsend) return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems + if (msecstouse == 12) + msecstouse = 13; + // if (msecstouse > 127) // Con_Printf("%i\n", msecstouse, msecs); @@ -2003,6 +2025,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) // // deliver the message // + cls.netchan.dupe = cl_c2sdupe.ival; Netchan_Transmit (&cls.netchan, buf.cursize, buf.data, 2500); if (cls.netchan.fatal_error) @@ -2051,6 +2074,7 @@ void CL_InitInput (void) Cvar_Register (&prox_inmenu, inputnetworkcvargroup); + Cvar_Register (&cl_c2sdupe, inputnetworkcvargroup); Cvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup); Cvar_Register (&cl_c2spps, inputnetworkcvargroup); Cvar_Register (&cl_queueimpulses, inputnetworkcvargroup); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index cb91ca7fb..4ffe62e5e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -4861,8 +4861,8 @@ double Host_Frame (double time) ) { // realtime += spare/1000; //don't use it all! - spare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver); - if (!spare) + double newspare = CL_FilterTime((spare/1000 + realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver); + if (!newspare) { while(COM_DoWork(0, false)) ; @@ -4872,6 +4872,7 @@ double Host_Frame (double time) spare = 0; //uncapped. if (spare > cl_sparemsec.ival) spare = cl_sparemsec.ival; + spare = newspare; // realtime -= spare/1000; //don't use it all! } @@ -4889,7 +4890,7 @@ double Host_Frame (double time) CL_ProgressDemoTime(); hadwork = haswork; } - cl.stillloading = cl.sendprespawn || (cls.state < ca_active && COM_HasWork()); + cl.stillloading = cl.sendprespawn || (cls.state < ca_active && worker_flush.ival && COM_HasWork()); COM_MainThreadWork(); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 93b4e76bc..598c12be2 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -5835,7 +5835,7 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n { flocation_t loc; Cmd_TokenizeString(stufftext+2, false, false); - if (FS_FLocateFile(Cmd_Argv(1), FSLFRT_IFFOUND, &loc)) + if (FS_FLocateFile(Cmd_Argv(1), FSLF_IFFOUND, &loc)) Con_Printf("You have been kicked due to the file \"%s\" being modified.\n", Cmd_Argv(1)); } #ifdef PLUGINS @@ -6568,13 +6568,13 @@ void CLQW_ParseServerMessage (void) case svcfte_cgamepacket: csqcpacket = true; -#ifdef CSQC_DAT - if (CSQC_ParseGamePacket()) - break; -#endif #ifdef HLCLIENT if (CLHL_ParseGamePacket()) break; +#endif +#ifdef CSQC_DAT + if (CSQC_ParseGamePacket()) + break; #endif Con_Printf("Unable to parse gamecode packet\n"); break; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 8cc055381..9ef528d3d 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1246,7 +1246,6 @@ void CL_PredictMovePNum (int seat) { for (i=0 ; i<3 ; i++) { - extern cvar_t temp1; pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i]; pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i]; diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 681b6df45..28f25014b 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1096,6 +1096,7 @@ void CL_ParseTEnt (void) if (nqprot) { //easiest way to handle these + //should probably also do qwgunshot ones with nq protocols or something switch(type) { case TENQ_EXPLOSION2: @@ -1107,10 +1108,41 @@ void CL_ParseTEnt (void) case TE_EXPLOSION: type = TEQW_EXPLOSIONNOSPRITE; break; + case TE_GUNSHOT: + type = TE_GUNSHOT_NQCOMPAT; + break; + case TE_GUNSHOT_NQCOMPAT: + type = TE_GUNSHOT; + break; default: break; } } + + //right, nq vs qw doesn't matter now, supposedly. + + if (cl_shownet.ival >= 2) + { + static char *te_names[] = { + "spike", "superspike", "qwgunshot", "qwexplosion", + "tarexplosion", "lightning1", "lightning2", "wizspike", + "knightspike", "lightning3", "lavasplash", "teleport", + "blood", "lightningblood", "bullet", "superbullet", //bullets deprecated + "railtrail", "beam", "explosion2", "nqexplosion", + "nqgunshot", "?", "?", "?", +#ifdef HEXEN2 + "h2lightsml", "h2chain", "h2sunstf1", "h2sunstf2", + "h2light", "h2cb", "h2ic", "h2gaze", + "h2famine", "h2partexp" +#endif + }; + + if (type < countof(te_names)) + Con_Printf(" te_%s\n", te_names[type]); + else + Con_Printf(" te_%i\n", type); + } + switch (type) { case TE_WIZSPIKE: // spike hitting wall @@ -1509,8 +1541,10 @@ void CL_ParseTEnt (void) P_RunParticleEffect (pos, vec3_origin, 0, 20); break; + case TE_GUNSHOT: // bullet hitting wall - if (nqprot) + case TE_GUNSHOT_NQCOMPAT: + if (type == TE_GUNSHOT_NQCOMPAT) cnt = 1; else cnt = MSG_ReadByte (); @@ -3301,7 +3335,7 @@ fixme: ex = CL_AllocExplosion (pos); ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; - CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0.1, 0); + CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_muzzleflash"); break; case CRTE_BLUE_MUZZLEFLASH: @@ -3309,7 +3343,7 @@ fixme: ex = CL_AllocExplosion (pos); ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; - CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0.1, 0); + CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_blue_muzzleflash"); break; case CRTE_SMART_MUZZLEFLASH: @@ -3317,7 +3351,7 @@ fixme: ex = CL_AllocExplosion (pos); ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; - CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0, 0.2); + CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_smart_muzzleflash"); break; case CRTE_LEADERFIELD: @@ -3328,7 +3362,7 @@ fixme: VectorCopy (pos, ex->origin); ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; - CL_NewDlightRGB(0, pos, 350, 0.5, 0.2, 0, 0.2); + CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_deathfield"); break; case CRTE_BLASTERBEAM: diff --git a/engine/client/clhl_game.c b/engine/client/clhl_game.c index 2f0b9df39..2d2640359 100644 --- a/engine/client/clhl_game.c +++ b/engine/client/clhl_game.c @@ -309,8 +309,8 @@ typedef struct void (QDECL *player_setkey) (char *key, char *value); //wait, no pnum? qboolean (QDECL *getcdkey) (int playernum, char key[16]); - int trackerfromplayer; - int playerfromtracker; + int (QDECL *trackerfromplayer) (int pl); + int (QDECL *playerfromtracker) (int tr); int (QDECL *sendcmd_unreliable) (char *cmd); void (QDECL *getsysmousepos) (long *xandy); void (QDECL *setsysmousepos) (int x, int y); @@ -707,8 +707,8 @@ void QDECL CLGHL_getplayerinfo (int entnum, hlplayerinfo_t *result) result->isus = true; result->isspec = player->spectator; result->pl = player->pl; - if (player->skin) - result->model = player->skin->name; + if (player->qwskin) + result->model = player->qwskin->name; else result->model = ""; } @@ -722,7 +722,7 @@ void QDECL CLGHL_startsound_name (char *name, float vol) Con_Printf ("CLGHL_startsound_name: can't cache %s\n", name); return; } - S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0); + S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0); } void QDECL CLGHL_startsound_idx (int idx, float vol) { @@ -733,7 +733,7 @@ void QDECL CLGHL_startsound_idx (int idx, float vol) Con_Printf ("CLGHL_startsound_name: index not precached %s\n", name); return; } - S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0); + S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0); } void QDECL CLGHL_anglevectors (float *ina, float *outf, float *outr, float *outu) @@ -1175,8 +1175,8 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs = CLGHL_player_setkey, CLGHL_getcdkey, - (void*)0xdeaddead,//CLGHL_trackerfromplayer; - (void*)0xdeaddead,//CLGHL_playerfromtracker; + CLGHL_trackerfromplayer, + CLGHL_playerfromtracker, CLGHL_sendcmd_unreliable, CLGHL_getsysmousepos, CLGHL_setsysmousepos, @@ -1186,7 +1186,7 @@ CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs = 0xdeadbeef }; -dllhandle_t clg; +dllhandle_t *clg; int CLHL_GamecodeDoesMouse(void) { @@ -1270,7 +1270,8 @@ void CLHL_LoadClientGame(void) clg = NULL; iterator = NULL; - while(COM_IteratePaths(&iterator, path, sizeof(path))) + //FIXME: dlls in gamepaths is evil + while(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0)) { snprintf (fullname, sizeof(fullname), "%s%s", path, "cl_dlls/client"); clg = Sys_LoadLibrary(fullname, funcs); @@ -1308,6 +1309,20 @@ void CLHL_LoadClientGame(void) CLHL_cgamefuncs.HUD_Init(); if (CLHL_cgamefuncs.HUD_VidInit) CLHL_cgamefuncs.HUD_VidInit(); + { + struct hlkbutton_s + { + int down[2]; // key nums holding it down + int state; // low bit is down state + } *but; + struct hlkbutton_s *(QDECL *pKB_Find) (const char *foo) = (void*)Sys_GetAddressForName(clg, "KB_Find"); + if (pKB_Find) + { + but = pKB_Find("in_mlook"); + if (but) + but->state |= 1; + } + } } int CLHL_BuildUserInput(int msecs, usercmd_t *cmd) @@ -1329,7 +1344,7 @@ int CLHL_BuildUserInput(int msecs, usercmd_t *cmd) cmd->upmove = hlcmd.upmove; cmd->weapon = hlcmd.weaponselect; cmd->impulse = hlcmd.impulse; - cmd->buttons = hlcmd.buttons; + cmd->buttons = hlcmd.buttons & 0xff; //FIXME: quake's protocols are more limited than this cmd->lightlevel = hlcmd.lightlevel; return true; #else @@ -1365,7 +1380,7 @@ int CLHL_DrawHud(void) CLHL_cgamefuncs.HUD_UpdateClientData(&state, cl.time); - ret = CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermission); + ret = CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermissionmode != IM_NONE); return ret; } @@ -1383,7 +1398,7 @@ int CLHL_AnimateViewEntity(entity_t *ent) return true; } -explosion_t *CL_AllocExplosion (void); +explosion_t *CL_AllocExplosion (vec3_t org); int CLHL_ParseGamePacket(void) { @@ -1445,19 +1460,18 @@ int CLHL_ParseGamePacket(void) if (!(flags & 8)) P_RunParticleEffectType(startp, NULL, 1, pt_explosion); if (!(flags & 4)) - S_StartSound(0, 0, S_PrecacheSound("explosion"), startp, 1, 1, 0, 0); + S_StartSound(0, 0, S_PrecacheSound("explosion"), startp, 1, 1, 0, 0, 0); if (!(flags & 2)) CL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0); - ef = CL_AllocExplosion(); - VectorCopy(startp, ef->origin); + ef = CL_AllocExplosion(startp); ef->start = cl.time; ef->model = cl.model_precache[midx]; ef->framerate = mrate; ef->firstframe = 0; ef->numframes = ef->model->numframes; if (!(flags & 1)) - ef->flags = Q2RF_ADDITIVE; + ef->flags = RF_ADDITIVE; else ef->flags = 0; break; @@ -1503,8 +1517,7 @@ int CLHL_ParseGamePacket(void) MSG_ReadByte(); lifetime = MSG_ReadByte(); - ef = CL_AllocExplosion(); - VectorCopy(startp, ef->origin); + ef = CL_AllocExplosion(startp); ef->start = cl.time; ef->angles[1] = ang; ef->model = cl.model_precache[midx]; @@ -1554,7 +1567,7 @@ int CLHL_ParseGamePacket(void) break; case svc_intermission: //nothing. - cl.intermission = true; + cl.intermissionmode = IM_NQSCORES; break; case svc_cdtrack: { @@ -1572,7 +1585,7 @@ int CLHL_ParseGamePacket(void) break; case 37: //svc_roomtype tempi = MSG_ReadShort(); - S_SetUnderWater(tempi==14||tempi==15||tempi==16); +// S_SetUnderWater(tempi==14||tempi==15||tempi==16); break; default: Con_Printf("Unrecognised gamecode packet %i (%s)\n", subcode, usermsgs[subcode].name); diff --git a/engine/client/client.h b/engine/client/client.h index acd74f4ed..4328ef807 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -303,6 +303,9 @@ typedef struct dlight_s unsigned int flags; char cubemapname[64]; + int coronaocclusionquery; + unsigned int coronaocclusionresult; + //the following are used for rendering (client code should clear on create) qboolean rebuildcache; struct shadowmesh_s *worldshadowmesh; @@ -975,7 +978,6 @@ void CL_FreeDlights(void); dlight_t *CL_AllocDlight (int key); dlight_t *CL_AllocSlight (void); //allocates a static light dlight_t *CL_NewDlight (int key, const vec3_t origin, float radius, float time, float r, float g, float b); -dlight_t *CL_NewDlightRGB (int key, const vec3_t origin, float radius, float time, float r, float g, float b); dlight_t *CL_NewDlightCube (int key, const vec3_t origin, vec3_t angles, float radius, float time, vec3_t colours); void CL_DecayLights (void); diff --git a/engine/client/console.c b/engine/client/console.c index 3db6f4e38..be260f607 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -971,6 +971,17 @@ void VARGS Con_SafeTPrintf (translation_t text, ...) Con_Printf ("%s", msg); } +static void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b) +{ + if (!developer.value) + Con_Log(data); + else + { + Sys_Printf ("%s", data); // also echo to debugging console + Con_PrintCon(&con_main, data, con_main.parseflags); + } + BZ_Free(data); +} /* ================ Con_DPrintf @@ -999,6 +1010,13 @@ void VARGS Con_DPrintf (const char *fmt, ...) vsnprintf (msg,sizeof(msg)-1, fmt,argptr); va_end (argptr); + if (!Sys_IsMainThread()) + { + if (developer.ival) + COM_AddWork(0, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0); + return; + } + if (!developer.value) Con_Log(msg); else diff --git a/engine/client/image.c b/engine/client/image.c index fddfa7420..9c4facc1d 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -28,7 +28,9 @@ char *r_defaultimageextensions = #if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB) " jpg" //q3 uses some jpegs, for some reason #endif +#ifndef NOLEGACY " pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs. +#endif ; static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue); cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load."); @@ -2783,7 +2785,9 @@ static struct {3, "textures/%s/%s%s", 1}, /*fuhquake compatibility*/ {3, "%s/%s%s", 1}, /*fuhquake compatibility*/ {2, "textures/%s%s", 1}, /*directly named texture with textures/ prefix*/ +#ifndef NOLEGACY {2, "override/%s%s", 1} /*tenebrae compatibility*/ +#endif }; static void Image_MipMap8888 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) @@ -3676,6 +3680,54 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag freedata = true; break; + case TF_MIP4_8PAL24: + //8bit opaque data + { + unsigned int pixels = + (imgwidth>>0) * (imgheight>>0) + + (imgwidth>>1) * (imgheight>>1) + + (imgwidth>>2) * (imgheight>>2) + + (imgwidth>>3) * (imgheight>>3); + palettedata = (qbyte*)rawdata + pixels; + Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags); + flags |= IF_NOPICMIP; + if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && sh_config.texfmt[PTI_RGBX8]) + { + unsigned int pixels = + (imgwidth>>0) * (imgheight>>0) + + (imgwidth>>1) * (imgheight>>1) + + (imgwidth>>2) * (imgheight>>2) + + (imgwidth>>3) * (imgheight>>3); + + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(pixels*4); + for (i = 0; i < pixels; i++) + { + qbyte *p = ((qbyte*)palettedata) + ((qbyte*)rawdata)[i]*3; + //FIXME: endian + rgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16); + } + + for (i = 0; i < 4; i++) + { + mips->mip[i].width = imgwidth>>i; + mips->mip[i].height = imgheight>>i; + mips->mip[i].datasize = mips->mip[i].width * mips->mip[i].height * 4; + mips->mip[i].needfree = false; + } + mips->mipcount = i; + mips->mip[0].data = rgbadata; + mips->mip[1].data = (qbyte*)mips->mip[0].data + mips->mip[0].datasize; + mips->mip[2].data = (qbyte*)mips->mip[1].data + mips->mip[1].datasize; + mips->mip[3].data = (qbyte*)mips->mip[2].data + mips->mip[2].datasize; + + mips->extrafree = rgbadata; + if (freedata) + BZ_Free(rawdata); + return true; + } + } + //fall through case TF_8PAL24: if (!palettedata) { @@ -4458,6 +4510,8 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned case TF_BGRA32: b *= 4; break; + case TF_MIP4_8PAL24: + pb = 3*256; case TF_MIP4_LUM8: case TF_MIP4_SOLID8: b = (fallbackwidth>>0)*(fallbackheight>>0) + @@ -4508,9 +4562,9 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned else #endif if (lowpri) - COM_AddWork(5, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); + COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); else - COM_AddWork(2+(seq++%3), Image_LoadHiResTextureWorker, tex, NULL, 0, 0); + COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); } return tex; } @@ -4711,6 +4765,7 @@ void Image_List_f(void) //may not create any images yet. void Image_Init(void) { + wadmutex = Sys_CreateMutex(); memset(imagetablebuckets, 0, sizeof(imagetablebuckets)); Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets); @@ -4736,6 +4791,10 @@ void Image_Shutdown(void) } if (i) Con_DPrintf("Destroyed %i/%i images\n", j, i); + + if (wadmutex) + Sys_DestroyMutex(wadmutex); + wadmutex = NULL; } //load the named file, without failing. diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index a4b4c265e..f53a08405 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -433,6 +433,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) int mfwt; qboolean strafe_x, strafe_y; int wpnum; + extern qboolean runningindepphys; //small performance boost if (mouse->type == M_INVALID) @@ -570,7 +571,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) #ifdef PEXT_CSQC if (mouse->type == M_TOUCH) { - if (CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid)) + if (!runningindepphys && CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid)) { mx = 0; my = 0; @@ -579,7 +580,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) else { if (mx || my) - if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) + if (!runningindepphys && CSQC_MouseMove(mx, my, mouse->qdeviceid)) { mx = 0; my = 0; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 6872ee33c..0c5a984b5 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -1651,7 +1651,7 @@ cin_t *Media_WinAvi_TryLoad(char *name) return NULL; - FS_FLocateFile(name, FSLFRT_DEPTH_OSONLY, &loc); + FS_FLocateFile(name, FSLF_IFFOUND, &loc); if (!loc.offset && *loc.rawname && !qAVIFileOpenA(&pavi, loc.rawname, OF_READ, NULL))//!AVIStreamOpenFromFile(&pavi, name, streamtypeVIDEO, 0, OF_READ, NULL)) { @@ -4424,6 +4424,15 @@ static void S_MP3_Purge(sfx_t *sfx) sfx->loadstate = SLS_NOTLOADED; } +float S_MP3_Query(sfx_t *sfx, sfxcache_t *buf) +{ + //we don't know unless we decode it all + if (buf) + { + } + return 0; +} + /*must be thread safe*/ sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length) { @@ -4552,6 +4561,7 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed) s->decoder.ended = S_MP3_Purge; s->decoder.purge = S_MP3_Purge; s->decoder.decodedata = S_MP3_Locate; + s->decoder.querydata = S_MP3_Query; dec->dstdata = NULL; dec->dstcount = 0; diff --git a/engine/client/merged.h b/engine/client/merged.h index 8a780724e..5dd5d038a 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -190,6 +190,7 @@ typedef enum uploadfmt TF_LUM8, /*8bit greyscale image*/ TF_MIP4_LUM8, /*8bit 4-mip greyscale image*/ TF_MIP4_SOLID8, /*8bit 4-mip image*/ + TF_MIP4_8PAL24, /*8bit 4-mip image*/ TF_SOLID8, /*8bit quake-palette image*/ TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/ TF_TRANS8_FULLBRIGHT, /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/ @@ -427,7 +428,7 @@ typedef struct rendererinfo_s { void (*BE_SubmitBatch)(struct batch_s *batch); struct batch_s *(*BE_GetTempBatch)(void); //Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required - void (*BE_DrawWorld) (qboolean drawworld, qbyte *vis); + void (*BE_DrawWorld) (struct batch_s **worldbatches, qbyte *vis); //called at init, force the display to the right defaults etc void (*BE_Init)(void); //Generates an optimised VBO, one for each texture on the map diff --git a/engine/client/p_script.c b/engine/client/p_script.c index c644d4488..8dbb8f160 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -227,6 +227,7 @@ typedef struct part_type_s { float orgadd, randomorgadd; //spawn the particle this far along its velocity direction float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards) vec3_t orgbias; //static 3d world-coord bias + float viewspacefrac; float s1, t1, s2, t2; //texture coords float texsstride; //addition for s for each random slot. @@ -316,6 +317,7 @@ typedef struct part_type_s { #define PT_TROVERWATER 0x0200 // don't spawn if underwater #define PT_TRUNDERWATER 0x0400 // don't spawn if overwater #define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows. + unsigned int fluidmask; unsigned int state; #define PS_INRUNLIST 0x1 // particle type is currently in execution list @@ -953,6 +955,7 @@ static void P_ResetToDefaults(part_type_t *ptype) ptype->inwater = P_INVALID; ptype->cliptype = P_INVALID; ptype->emit = P_INVALID; + ptype->fluidmask = FTECONTENTS_FLUID; ptype->alpha = 1; ptype->alphachange = 1; ptype->clipbounce = 0.8; @@ -1243,7 +1246,7 @@ void P_ParticleEffect_f(void) ptype->alpha = atof(value); else if (!strcmp(var, "alphachange")) { - Con_DPrintf("alphachange is deprecated, use alphadelta\n"); + Con_DPrintf("%s.%s: alphachange is deprecated, use alphadelta\n", ptype->config, ptype->name); ptype->alphachange = atof(value); } else if (!strcmp(var, "alphadelta")) @@ -1268,7 +1271,7 @@ void P_ParticleEffect_f(void) } else if (!strcmp(var, "diesubrand")) { - Con_DPrintf("diesubrand is deprecated, use die with two arguments\n"); + Con_DPrintf("%s.%s: diesubrand is deprecated, use die with two arguments\n", ptype->config, ptype->name); ptype->randdie = atof(value); } @@ -1339,6 +1342,49 @@ void P_ParticleEffect_f(void) ptype = &part_type[pnum]; ptype->inwater = assoc; } + else if (!strcmp(var, "underwater")) + { + ptype->flags |= PT_TRUNDERWATER; + +parsefluid: + if ((ptype->flags & (PT_TRUNDERWATER|PT_TROVERWATER)) == (PT_TRUNDERWATER|PT_TROVERWATER)) + { + ptype->flags &= ~PT_TRUNDERWATER; + Con_Printf("%s.%s: both over and under water\n", ptype->config, ptype->name); + } + if (Cmd_Argc() == 1) + ptype->fluidmask = FTECONTENTS_FLUID; + else + { + int i = Cmd_Argc(); + ptype->fluidmask = 0; + while (i --> 1) + { + char *value = Cmd_Argv(i); + if (!strcmp(value, "water")) + ptype->fluidmask |= FTECONTENTS_WATER; + else if (!strcmp(value, "slime")) + ptype->fluidmask |= FTECONTENTS_SLIME; + else if (!strcmp(value, "lava")) + ptype->fluidmask |= FTECONTENTS_LAVA; + else if (!strcmp(value, "sky")) + ptype->fluidmask |= FTECONTENTS_SKY; + else if (!strcmp(value, "fluid")) + ptype->fluidmask |= FTECONTENTS_FLUID; + else if (!strcmp(value, "solid")) + ptype->fluidmask |= FTECONTENTS_SOLID; + else if (!strcmp(value, "playerclip")) + ptype->fluidmask |= FTECONTENTS_PLAYERCLIP; + else + Con_Printf("%s.%s: unknown contents: %s\n", ptype->config, ptype->name, value); + } + } + } + else if (!strcmp(var, "notunderwater")) + { + ptype->flags |= PT_TROVERWATER; + goto parsefluid; + } else if (!strcmp(var, "model")) { partmodels_t *mod; @@ -1623,7 +1669,7 @@ void P_ParticleEffect_f(void) } else if (!strcmp(var, "isbeam")) { - Con_DPrintf("isbeam is deprecated, use type beam\n"); + Con_DPrintf("%s.%s: isbeam is deprecated, use type beam\n", ptype->config, ptype->name); ptype->looks.type = PT_BEAM; } else if (!strcmp(var, "spawntime")) @@ -1662,22 +1708,22 @@ void P_ParticleEffect_f(void) // old names else if (!strcmp(var, "areaspread")) { - Con_DPrintf("areaspread is deprecated, use spawnorg\n"); + Con_DPrintf("%s.%s: areaspread is deprecated, use spawnorg\n", ptype->config, ptype->name); ptype->areaspread = atof(value); } else if (!strcmp(var, "areaspreadvert")) { - Con_DPrintf("areaspreadvert is deprecated, use spawnorg\n"); + Con_DPrintf("%s.%s: areaspreadvert is deprecated, use spawnorg\n", ptype->config, ptype->name); ptype->areaspreadvert = atof(value); } else if (!strcmp(var, "offsetspread")) { - Con_DPrintf("offsetspread is deprecated, use spawnvel\n"); + Con_DPrintf("%s.%s: offsetspread is deprecated, use spawnvel\n", ptype->config, ptype->name); ptype->spawnvel = atof(value); } else if (!strcmp(var, "offsetspreadvert")) { - Con_DPrintf("offsetspreadvert is deprecated, use spawnvel\n"); + Con_DPrintf("%s.%s: offsetspreadvert is deprecated, use spawnvel\n", ptype->config, ptype->name); ptype->spawnvelvert = atof(value); } @@ -1713,7 +1759,7 @@ void P_ParticleEffect_f(void) ptype->rampmode = RAMP_NONE; else if (!strcmp(value, "absolute")) { - Con_DPrintf("'rampmode absolute' is deprecated, use 'rampmode nearest'\n"); + Con_DPrintf("%s.%s: 'rampmode absolute' is deprecated, use 'rampmode nearest'\n", ptype->config, ptype->name); ptype->rampmode = RAMP_NEAREST; } else if (!strcmp(value, "nearest")) @@ -1805,6 +1851,8 @@ void P_ParticleEffect_f(void) ptype->rampindexes++; } + else if (!strcmp(var, "viewspace")) + ptype->viewspacefrac = (Cmd_Argc()>1)?atof(value):1; else if (!strcmp(var, "perframe")) ptype->flags |= PT_INVFRAMETIME; else if (!strcmp(var, "averageout")) @@ -1819,7 +1867,7 @@ void P_ParticleEffect_f(void) else if (!strcmp(var, "lightradius")) { //float version ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value); - if (Cmd_Argc()>3) + if (Cmd_Argc()>2) ptype->dl_radius[1] = atof(Cmd_Argv(2)); ptype->dl_radius[1] -= ptype->dl_radius[0]; } @@ -1862,7 +1910,7 @@ void P_ParticleEffect_f(void) ptype->stain_rgb[2] = atof(Cmd_Argv(4)); } else - Con_DPrintf("%s is not a recognised particle type field (in %s)\n", var, ptype->name); + Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var); } ptype->looks.invscalefactor = 1-ptype->looks.scalefactor; ptype->loaded = part_parseweak?1:2; @@ -1883,12 +1931,12 @@ void P_ParticleEffect_f(void) if (ptype->scale) { ptype->looks.type = PT_SPARKFAN; - Con_DPrintf("effect %s lacks a texture. assuming type sparkfan.\n", ptype->name); + Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name); } else { ptype->looks.type = PT_SPARK; - Con_DPrintf("effect %s lacks a texture. assuming type spark.\n", ptype->name); + Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name); } } else if (ptype->looks.type == PT_SPARK) @@ -1924,11 +1972,11 @@ void P_ParticleEffect_f(void) if (ptype->rampmode && !ptype->ramp) { ptype->rampmode = RAMP_NONE; - Con_Printf("Particle type %s has a ramp mode but no ramp\n", ptype->name); + Con_Printf("%s.%s: Particle has a ramp mode but no ramp\n", ptype->config, ptype->name); } else if (ptype->ramp && !ptype->rampmode) { - Con_Printf("Particle type %s has a ramp but no ramp mode\n", ptype->name); + Con_Printf("%s.%s: Particle has a ramp but no ramp mode\n", ptype->config, ptype->name); } P_LoadTexture(ptype, true); @@ -2108,6 +2156,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) if (ptype->areaspread || ptype->areaspreadvert) Q_strncatz(outstr, va("spawnorg %g %g\n", ptype->areaspread, ptype->areaspreadvert), outstrlen); + if (ptype->viewspacefrac) + Q_strncatz(outstr, ((ptype->viewspacefrac==1)?"viewspace\n":va("viewspace %g\n", ptype->viewspacefrac)), outstrlen); + if (ptype->veladd || ptype->randomveladd) { if (ptype->randomveladd) @@ -3920,9 +3971,9 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int cont; cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org); - if ((ptype->flags & PT_TROVERWATER) && (cont & FTECONTENTS_FLUID)) + if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask)) goto skip; - if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID)) + if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask)) goto skip; } @@ -4724,9 +4775,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype int cont; cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos); - if ((ptype->flags & PT_TROVERWATER) && (cont & FTECONTENTS_FLUID)) + if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask)) return; - if ((ptype->flags & PT_TRUNDERWATER) && !(cont & FTECONTENTS_FLUID)) + if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask)) return; } @@ -5853,6 +5904,8 @@ static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type) static void PScript_DrawParticleTypes (void) { + float viewtranslation[16]; + static float lastviewmatrix[16]; // void (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle; void (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle; void (*sparktexturedparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTexturedSparkParticle; @@ -5924,6 +5977,14 @@ static void PScript_DrawParticleTypes (void) kill_list = kill_first = NULL; + + { + float tmp[16]; + Matrix4_Invert(r_refdef.m_view, tmp); + Matrix4_Multiply(tmp, lastviewmatrix, viewtranslation); + memcpy(lastviewmatrix, r_refdef.m_view, sizeof(tmp)); + } + for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun) { if (type->clippeddecals) @@ -6254,6 +6315,15 @@ static void PScript_DrawParticleTypes (void) p->vel[2] -= grav; } + if (type->viewspacefrac) + { + vec3_t tmp; + Matrix4x4_CM_Transform3(viewtranslation, p->org, tmp); + VectorInterpolate(p->org, type->viewspacefrac, tmp, p->org); + Matrix4x4_CM_Transform3x3(viewtranslation, p->vel, tmp); + VectorInterpolate(p->vel, type->viewspacefrac, tmp, p->vel); + } + p->angle += p->rotationspeed*pframetime; switch (type->rampmode) diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 54be15159..e0455c427 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -540,16 +540,26 @@ void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_g const char *sample = PR_GetStringOfs(prinst, OFS_PARM0); sfx_t *sfx = S_PrecacheSound(sample); + if (sfx && sfx->loadstate == SLS_LOADING) + COM_WorkerPartialSync(sfx, &sfx->loadstate, SLS_LOADING); if (!sfx || sfx->loadstate != SLS_LOADED) G_FLOAT(OFS_RETURN) = 0; else { sfxcache_t cachebuf, *cache; - if (sfx->decoder.decodedata) + if (sfx->decoder.querydata) + { + G_FLOAT(OFS_RETURN) = sfx->decoder.querydata(sfx, NULL); + return; + } + else if (sfx->decoder.decodedata) cache = sfx->decoder.decodedata(sfx, &cachebuf, 0x7ffffffe, 0); else cache = sfx->decoder.buf; - G_FLOAT(OFS_RETURN) = (cache->soundoffset+cache->length) / (float)snd_speed; + if (!cache) + G_FLOAT(OFS_RETURN) = 0; + else + G_FLOAT(OFS_RETURN) = (cache->soundoffset+cache->length) / (float)snd_speed; } } diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 4215332a4..9227c73c2 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1864,6 +1864,9 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars csqc_worldchanged = false; Surf_NewMap(); CL_UpdateWindowTitle(); + + World_RBE_Shutdown(&csqc_world); + World_RBE_Start(&csqc_world); } if (cl.worldmodel) diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 35905caf6..15b8c06b5 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -2327,6 +2327,15 @@ pbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc) return crc == 10020; } +static int QDECL MP_PRFileSize (const char *path) +{ + flocation_t loc; + if (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc)) + return loc.len; + else + return -1; +} + double menutime; qboolean MP_Init (void) { @@ -2350,7 +2359,7 @@ qboolean MP_Init (void) menuprogparms.progsversion = PROGSTRUCT_VERSION; menuprogparms.ReadFile = COM_LoadStackFile;//char *(*ReadFile) (char *fname, void *buffer, int *len); - menuprogparms.FileSize = COM_FileSize;//int (*FileSize) (char *fname); //-1 if file does not exist + menuprogparms.FileSize = MP_PRFileSize;//int (*FileSize) (char *fname); //-1 if file does not exist menuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len); menuprogparms.Printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...); menuprogparms.Sys_Error = Sys_Error; diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 34ae3a0a9..841910407 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -27,6 +27,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "com_mesh.h" #include +#if defined(GLQUAKE) && defined(MULTITHREAD) +#define THREADEDWORLD +#endif + extern cvar_t r_ambient; static vec3_t modelorg; /*set before recursively entering the visible surface finder*/ @@ -574,7 +578,7 @@ static void Surf_AddDynamicLightsColours (msurface_t *surf) -static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwidth) +static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwidth, vec3_t *blocknormals) { int smax, tmax; int i, j, size; @@ -704,11 +708,10 @@ enum lm_mode just unpacks the internal lightmap block into texture info ready for upload merges stains and oversaturates overbrights. */ -static void Surf_StoreLightmap(qbyte *dest, int smax, int tmax, unsigned int shift, enum lm_mode lm_mode, stmap *stainsrc, unsigned int lmwidth) +static void Surf_StoreLightmap(qbyte *dest, unsigned int *bl, int smax, int tmax, unsigned int shift, enum lm_mode lm_mode, stmap *stainsrc, unsigned int lmwidth) { int r, g, b, t, m; unsigned int i, j; - unsigned int *bl; int stride; switch (lm_mode) @@ -716,8 +719,6 @@ static void Surf_StoreLightmap(qbyte *dest, int smax, int tmax, unsigned int shi case bgra4_os: stride = lmwidth*4 - (smax<<2); - bl = blocklights; - for (i=0 ; ideluxdata) - Surf_BuildDeluxMap(surf, deluxdest, lmwidth); + Surf_BuildDeluxMap(surf, deluxdest, lmwidth, blocknormals); #ifdef PEXT_LIGHTSTYLECOL if (lightmap_bytes == 4 || lightmap_bytes == 3) @@ -1082,14 +1081,14 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest, { if (lightmap_bgra) { - Surf_StoreLightmap(dest, smax, tmax, shift, bgra4_os, stainsrc, lmwidth); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgra4_os, stainsrc, lmwidth); } else { /*if (!r_stains.value || !surf->stained) - Surf_StoreLightmap(dest, smax, tmax, shift, rgba4, NULL); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgba4, NULL); else - Surf_StoreLightmap(dest, smax, tmax, shift, rgba4, stainsrc); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgba4, stainsrc); */ } } @@ -1099,14 +1098,14 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest, { /* if (!r_stains.value || !surf->stained) - Surf_StoreLightmap(dest, smax, tmax, shift, bgr3, NULL); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgr3, NULL); else - Surf_StoreLightmap(dest, smax, tmax, shift, bgr3, stainsrc); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgr3, stainsrc); */ } else { - Surf_StoreLightmap(dest, smax, tmax, shift, rgb3_os, stainsrc, lmwidth); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgb3_os, stainsrc, lmwidth); } } } @@ -1166,7 +1165,303 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest, Surf_AddDynamicLights (surf); } - Surf_StoreLightmap(dest, smax, tmax, shift, lum, stainsrc, lmwidth); + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, lum, stainsrc, lmwidth); + } +} + + +static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *deluxdest, stmap *stainsrc, int shift, int ambient, unsigned int lmwidth) +{ + int smax, tmax; + int t; + int i, j; + size_t size; + qbyte *lightmap; + unsigned scale; + int maps; + unsigned *bl; + + static size_t maxblocksize; + static vec3_t *blocknormals; + static unsigned int *blocklights; + + //int stride = LMBLOCK_WIDTH*lightmap_bytes; //warning: unused variable ‘stride’ + + shift += 7; // increase to base value + surf->cached_dlight = false; + + smax = (surf->extents[0]>>surf->lmshift)+1; + tmax = (surf->extents[1]>>surf->lmshift)+1; + size = (size_t)smax*tmax; + lightmap = surf->samples; + + if (size > maxblocksize) + { //fixme: fill in? + maxblocksize = size; + blocknormals = BZ_Realloc(blocknormals, maxblocksize * sizeof(*blocknormals)); //already a vector + blocklights = BZ_Realloc(blocklights, maxblocksize * 3*sizeof(*blocklights)); + } + + if (currentmodel->deluxdata) + Surf_BuildDeluxMap(surf, deluxdest, lmwidth, blocknormals); + +#ifdef PEXT_LIGHTSTYLECOL + if (lightmap_bytes == 4 || lightmap_bytes == 3) + { + // set to full bright if no light data + if (ambient < 0) + { + t = (-1-ambient)*255; + for (i=0 ; icached_light[maps] = -1-ambient; + surf->cached_colour[maps] = 0xff; + } + } + else if (r_fullbright.value>0) //not qw + { + for (i=0 ; ilightdata) + { + /*fullbright if map is not lit. but not overbright*/ + for (i=0 ; isamples) + { + /*no samples, but map is otherwise lit = pure black*/ + for (i=0 ; icached_light[0] = 0; + surf->cached_colour[0] = 0; + } + else + { +// clear to no light + t = ambient; + if (t == 0) + memset(blocklights, 0, size*3*sizeof(*bl)); + else + { + for (i=0 ; ifromgame == fg_quake3) //rgb + { + /*q3 lightmaps are meant to be pre-built + this code is misguided, and ought never be executed anyway. + */ + bl = blocklights; + for (i = 0; i < tmax; i++) + { + for (j = 0; j < smax; j++) + { + bl[0] = 255*lightmap[(i*lmwidth+j)*3]; + bl[1] = 255*lightmap[(i*lmwidth+j)*3+1]; + bl[2] = 255*lightmap[(i*lmwidth+j)*3+2]; + bl+=3; + } + } + } + else if (currentmodel->engineflags & MDLF_RGBLIGHTING) //rgb + { + for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey; + + if (scale) + { + if (cl_lightstyle[surf->styles[maps]].colours[0] == 1 && cl_lightstyle[surf->styles[maps]].colours[1] == 1 && cl_lightstyle[surf->styles[maps]].colours[2] == 1) //hopefully a faster alternative. + { + bl = blocklights; + for (i=0 ; istyles[maps]].colours[0]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[0]; + for (i=0 ; istyles[maps]].colours[1]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[1]; + for (i=0 ; istyles[maps]].colours[2]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[2]; + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey; + + if (cl_lightstyle[surf->styles[maps]].colours[0] == 1 && cl_lightstyle[surf->styles[maps]].colours[1] == 1 && cl_lightstyle[surf->styles[maps]].colours[2] == 1) //hopefully a faster alternative. + { + bl = blocklights; + for (i=0 ; istyles[maps]].colours[0]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[0]; + for (i=0, bl = blocklights; istyles[maps]].colours[1]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[1]; + for (i=0, bl = blocklights+1; istyles[maps]].colours[2]) + { + scale = d_lightstylevalue[surf->styles[maps]] * cl_lightstyle[surf->styles[maps]].colours[2]; + for (i=0, bl = blocklights+2; istained) + stainsrc = NULL; + + if (lightmap_bytes == 4) + { + if (lightmap_bgra) + { + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgra4_os, stainsrc, lmwidth); + } + else + { + /*if (!r_stains.value || !surf->stained) + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgba4, NULL); + else + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgba4, stainsrc); + */ + } + } + else if (lightmap_bytes == 3) + { + if (lightmap_bgra) + { + /* + if (!r_stains.value || !surf->stained) + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgr3, NULL); + else + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, bgr3, stainsrc); + */ + } + else + { + Surf_StoreLightmap(dest, blocklights, smax, tmax, shift, rgb3_os, stainsrc, lmwidth); + } + } + } + else +#endif + { + // set to full bright if no light data + if (!surf->samples || !currentmodel->lightdata) + { + for (i=0 ; icached_light[0] = d_lightstylevalue[0]; + surf->cached_colour[0] = cl_lightstyle[0].colourkey; + } + else if (r_fullbright.ival) + { + for (i=0 ; iengineflags & MDLF_RGBLIGHTING) //rgb + for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/3; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey; + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey; + for (i=0 ; iextents[1]>>fa->lmshift)+1; theRect = &lm->rectchange; - if (fa->light_t[0] < theRect->t) { - if (theRect->h) - theRect->h += theRect->t - fa->light_t[0]; + if (theRect->t > fa->light_t[0]) theRect->t = fa->light_t[0]; - } - if (fa->light_s[0] < theRect->l) { - if (theRect->w) - theRect->w += theRect->l - fa->light_s[0]; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; + if (theRect->l > fa->light_s[0]) theRect->l = fa->light_s[0]; - } - if ((theRect->w + theRect->l) < (fa->light_s[0] + smax)) - theRect->w = (fa->light_s[0]-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t[0] + tmax)) - theRect->h = (fa->light_t[0]-theRect->t)+tmax; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; if (lm->hasdeluxe) { dlm = lightmap[fa->lightmaptexturenums[0]+1]; dlm->modified = true; theRect = &dlm->rectchange; - if (fa->light_t[0] < theRect->t) { - if (theRect->h) - theRect->h += theRect->t - fa->light_t[0]; + if (theRect->t > fa->light_t[0]) theRect->t = fa->light_t[0]; - } - if (fa->light_s[0] < theRect->l) { - if (theRect->w) - theRect->w += theRect->l - fa->light_s[0]; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; + if (theRect->l > fa->light_s[0]) theRect->l = fa->light_s[0]; - } - - if ((theRect->w + theRect->l) < (fa->light_s[0] + smax)) - theRect->w = (fa->light_s[0]-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t[0] + tmax)) - theRect->h = (fa->light_t[0]-theRect->t)+tmax; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; luxbase = dlm->lightmaps; luxbase += (fa->light_t[0] * dlm->width + fa->light_s[0]) * lightmap_bytes; @@ -1287,6 +1569,90 @@ dynamic: } } +#ifdef THREADEDWORLD +static void Surf_RenderDynamicLightmaps_Worker (msurface_t *fa) +{ + qbyte *base, *luxbase; + stmap *stainbase; + int maps; + glRect_t *theRect; + int smax, tmax; + lightmapinfo_t *lm, *dlm; + + //surfaces without lightmaps + if (fa->lightmaptexturenums[0]<0 || !lightmap) + return; + + // check for lightmap modification + if (!fa->samples) + { + if (fa->cached_light[0] != 0 + || fa->cached_colour[0] != 0) + goto dynamic; + } + else + { + for (maps = 0 ; maps < MAXQ1LIGHTMAPS && fa->styles[maps] != 255 ; + maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] + || cl_lightstyle[fa->styles[maps]].colourkey != fa->cached_colour[maps]) + goto dynamic; + } + + return; + +dynamic: + +#ifdef _DEBUG + if ((unsigned)fa->lightmaptexturenums[0] >= numlightmaps) + Sys_Error("Invalid lightmap index\n"); +#endif + + lm = lightmap[fa->lightmaptexturenums[0]]; + + smax = (fa->extents[0]>>fa->lmshift)+1; + tmax = (fa->extents[1]>>fa->lmshift)+1; + + theRect = &lm->rectchange; + if (theRect->t > fa->light_t[0]) + theRect->t = fa->light_t[0]; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; + if (theRect->l > fa->light_s[0]) + theRect->l = fa->light_s[0]; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; + + if (lm->hasdeluxe) + { + dlm = lightmap[fa->lightmaptexturenums[0]+1]; + dlm->modified = true; + theRect = &dlm->rectchange; + if (theRect->t > fa->light_t[0]) + theRect->t = fa->light_t[0]; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; + if (theRect->l > fa->light_s[0]) + theRect->l = fa->light_s[0]; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; + + luxbase = dlm->lightmaps; + luxbase += (fa->light_t[0] * dlm->width + fa->light_s[0]) * lightmap_bytes; + } + else + luxbase = NULL; + + base = lm->lightmaps; + base += (fa->light_t[0] * lm->width + fa->light_s[0]) * lightmap_bytes; + stainbase = lm->stainmaps; + stainbase += (fa->light_t[0] * lm->width + fa->light_s[0]) * 3; + Surf_BuildLightMap_Worker (fa, base, luxbase, stainbase, lightmap_shift, r_ambient.value*255, lm->width); + + lm->modified = true; +} +#endif //THREADEDWORLD + void Surf_RenderAmbientLightmaps (msurface_t *fa, int ambient) { qbyte *base, *luxbase; @@ -1320,45 +1686,28 @@ dynamic: tmax = (fa->extents[1]>>fa->lmshift)+1; theRect = &lm->rectchange; - if (fa->light_t[0] < theRect->t) - { - if (theRect->h) - theRect->h += theRect->t - fa->light_t[0]; + if (theRect->t > fa->light_t[0]) theRect->t = fa->light_t[0]; - } - if (fa->light_s[0] < theRect->l) - { - if (theRect->w) - theRect->w += theRect->l - fa->light_s[0]; + if (theRect->l > fa->light_s[0]) theRect->l = fa->light_s[0]; - } - if ((theRect->w + theRect->l) < (fa->light_s[0] + smax)) - theRect->w = (fa->light_s[0]-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t[0] + tmax)) - theRect->h = (fa->light_t[0]-theRect->t)+tmax; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; if (lm->hasdeluxe) { dlm = lightmap[fa->lightmaptexturenums[0]+1]; lm->modified = true; theRect = &lm->rectchange; - if (fa->light_t[0] < theRect->t) - { - if (theRect->h) - theRect->h += theRect->t - fa->light_t[0]; + if (theRect->t > fa->light_t[0]) theRect->t = fa->light_t[0]; - } - if (fa->light_s[0] < theRect->l) - { - if (theRect->w) - theRect->w += theRect->l - fa->light_s[0]; + if (theRect->l > fa->light_s[0]) theRect->l = fa->light_s[0]; - } - - if ((theRect->w + theRect->l) < (fa->light_s[0] + smax)) - theRect->w = (fa->light_s[0]-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t[0] + tmax)) - theRect->h = (fa->light_t[0]-theRect->t)+tmax; + if (theRect->r < fa->light_s[0]+smax) + theRect->r = fa->light_s[0]+smax; + if (theRect->b < fa->light_t[0]+tmax) + theRect->b = fa->light_t[0]+tmax; luxbase = dlm->lightmaps; luxbase += (fa->light_t[0] * dlm->width + fa->light_s[0]) * lightmap_bytes; @@ -1919,7 +2268,7 @@ start: } #endif -static void Surf_PushChains(model_t *model) +static void Surf_PushChains(batch_t **batches) { batch_t *batch; int i; @@ -1930,7 +2279,7 @@ static void Surf_PushChains(model_t *model) if (!r_refdef.recurse) { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->firstmesh = 0; } @@ -1939,7 +2288,7 @@ static void Surf_PushChains(model_t *model) else if (r_refdef.recurse > 1) { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->recursefirst[r_refdef.recurse] = batch->firstmesh; batch->firstmesh = batch->meshes; @@ -1949,13 +2298,13 @@ static void Surf_PushChains(model_t *model) else { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->firstmesh = batch->meshes; } } } -static void Surf_PopChains(model_t *model) +static void Surf_PopChains(batch_t **batches) { batch_t *batch; int i; @@ -1963,7 +2312,7 @@ static void Surf_PopChains(model_t *model) if (!r_refdef.recurse) { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->meshes = 0; } @@ -1972,7 +2321,7 @@ static void Surf_PopChains(model_t *model) else if (r_refdef.recurse > 1) { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->meshes = batch->firstmesh; batch->firstmesh = batch->recursefirst[r_refdef.recurse]; @@ -1982,7 +2331,7 @@ static void Surf_PopChains(model_t *model) else { for (i = 0; i < SHADER_SORT_COUNT; i++) - for (batch = model->batches[i]; batch; batch = batch->next) + for (batch = batches[i]; batch; batch = batch->next) { batch->meshes = batch->firstmesh; batch->firstmesh = 0; @@ -2197,7 +2546,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent) currententity = ent; currentmodel = ent->model; - if (model->nummodelsurfaces != 0 && r_dynamic.ival) + if (model->nummodelsurfaces != 0 && r_dynamic.ival > 0) { for (k=rtlights_first; kebo); + BZ_Free(es); +} +void R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_) +{ + size_t idxcount; + unsigned int i; + model_t *mod; + batch_t *b, *batch; + mesh_t *m; + int sortid; + R_DestroyWorldEBO(webostate); + webostate = ctx; + webogenerating = NULL; + webogeneratingstate = 0; + mod = webostate->wmodel; + + qglGenBuffersARB(1, &webostate->ebo); + for (i = 0, idxcount = 0; i < webostate->numbatches; i++) + idxcount += webostate->batches[i].numidx; + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, webostate->ebo); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, idxcount*sizeof(index_t), NULL, GL_STATIC_DRAW_ARB); + for (i = 0, idxcount = 0; i < webostate->numbatches; i++) + { + qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, idxcount*sizeof(index_t), webostate->batches[i].numidx*sizeof(index_t), webostate->batches[i].idxbuffer); + BZ_Free(webostate->batches[i].idxbuffer); + webostate->batches[i].idxbuffer = (index_t*)NULL + idxcount; + idxcount += webostate->batches[i].numidx; + } + + //should be doing this on the worker, but whatever + for (i = 0, sortid = 0; sortid < SHADER_SORT_COUNT; sortid++) + { + webostate->rbatches[sortid] = NULL; + for (batch = mod->batches[sortid]; batch != NULL; batch = batch->next, i++) + { + if (!webostate->batches[i].numidx) + continue; + + m = &webostate->batches[i].m; + webostate->batches[i].pm = m; + b = &webostate->batches[i].b; + memcpy(b, batch, sizeof(*b)); + memset(m, 0, sizeof(*m)); + m->numvertexes = webostate->batches[i].b.vbo->vertcount; + b->mesh = &webostate->batches[i].pm; + b->meshes = 1; + m->numindexes = webostate->batches[i].numidx; + m->vbofirstelement = webostate->batches[i].idxbuffer - (index_t*)NULL; + m->vbofirstvert = 0; + m->indexes = NULL; + b->vbo->indicies.gl.vbo = webostate->ebo; + b->vbo->indicies.gl.addr = NULL; + b->vbo->vao = 0; + + b->next = webostate->rbatches[sortid]; + webostate->rbatches[sortid] = b; + } + } +} +static void Surf_SimpleWorld(struct webostate_s *es, qbyte *pvs) +{ + mleaf_t *leaf; + msurface_t *surf, **mark, **end; + mesh_t *mesh; + int l; + int fc = -r_framecount; + for (leaf = es->wmodel->leafs+es->wmodel->numclusters, l = es->wmodel->numclusters; l-- > 0; leaf--) + { + if ((pvs[l>>3] & (1u<<(l&7))) && leaf->nummarksurfaces) + { + mark = leaf->firstmarksurface; + end = mark+leaf->nummarksurfaces; + while(mark < end) + { + surf = *mark++; + if (surf->visframe != fc) + { + int i; + struct wesbatch_s *eb; + surf->visframe = fc; + Surf_RenderDynamicLightmaps_Worker (surf); + + mesh = surf->mesh; + eb = &es->batches[surf->sbatch->ebobatch]; + if (eb->maxidx < eb->numidx + mesh->numindexes) + { + //FIXME: pre-allocate +// continue; + eb->maxidx = eb->numidx + surf->mesh->numindexes; + eb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t)); + } + for (i = 0; i < mesh->numindexes; i++) + eb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + mesh->vbofirstvert; + eb->numidx += mesh->numindexes; + } + } + } + } +} +void R_GenWorldEBO(void *ctx, void *data, size_t a, size_t b) +{ + int i; + struct webostate_s *es = ctx; + qbyte *pvs; + + es->numbatches = es->wmodel->numbatches; + + //maybe we should just use fatpvs instead, and wait for completion when outside? + if (es->leaf[1]) + { //view is near to a water boundary. this implies the water crosses the near clip plane. + qbyte tmppvs[MAX_MAP_LEAFS/8]; + int c; + Q1BSP_LeafPVS (es->wmodel, es->leaf[0], es->pvs, sizeof(es->pvs)); + Q1BSP_LeafPVS (es->wmodel, es->leaf[1], tmppvs, sizeof(tmppvs)); + c = (es->wmodel->numclusters+31)/32; + for (i=0 ; ipvs)[i] |= ((int *)tmppvs)[i]; + pvs = es->pvs; + } + else + { + pvs = Q1BSP_LeafPVS (es->wmodel, es->leaf[0], es->pvs, sizeof(es->pvs)); + } + + for (i = 0; i < es->numbatches; i++) + { + es->batches[i].numidx = 0; + es->batches[i].maxidx = 0; + es->batches[i].idxbuffer = NULL; + } + + Surf_SimpleWorld(es, pvs); + + COM_AddWork(0, R_GeneratedWorldEBO, es, NULL, 0, 0); +} +#endif + /* ============= R_DrawWorld @@ -2295,12 +2813,13 @@ void Surf_DrawWorld (void) //surfvis vs entvis - the key difference is that surfvis is surfaces while entvis is volume. though surfvis should be frustum culled also for lighting. entvis doesn't care. qbyte *surfvis, *entvis; qbyte frustumvis_[MAX_MAP_LEAFS/8]; + extern cvar_t temp1; RSpeedLocals(); if (r_refdef.flags & RDF_NOWORLDMODEL) { r_refdef.flags |= RDF_NOWORLDMODEL; - BE_DrawWorld(false, NULL); + BE_DrawWorld(NULL, NULL); return; } if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) @@ -2317,7 +2836,71 @@ void Surf_DrawWorld (void) Surf_LightmapShift(cl.worldmodel); - Surf_PushChains(cl.worldmodel); +#ifdef THREADEDWORLD + if (r_dynamic.ival < 0 && !r_refdef.recurse && cl.worldmodel->type == mod_brush && cl.worldmodel->fromgame == fg_quake) + { + if (webostate && webostate->wmodel != cl.worldmodel) + { + R_DestroyWorldEBO(webostate); + webostate = NULL; + } + if (webostate && webostate->leaf[0] == r_viewleaf && webostate->leaf[1] == r_viewleaf2 && webostate->goodtime > cl.time) + { + } + else + { + if (!webogenerating) + { + if (!cl.worldmodel->numbatches) + { + int sortid; + batch_t *batch; + cl.worldmodel->numbatches = 0; + for (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++) + for (batch = cl.worldmodel->batches[sortid]; batch != NULL; batch = batch->next) + { + batch->ebobatch = cl.worldmodel->numbatches; + cl.worldmodel->numbatches++; + } + } + webogeneratingstate = true; + webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (cl.worldmodel->numbatches-1)); + webogenerating->wmodel = cl.worldmodel; + webogenerating->leaf[0] = r_viewleaf; + webogenerating->leaf[1] = r_viewleaf2; + webogenerating->goodtime = cl.time; //stupid lightstyle animations. + if (r_lightstylesmooth.ival) + webogenerating->goodtime += 1.0/60; + else + webogenerating->goodtime += 0.1; + Q_strncpyz(webogenerating->dbgid, "webostate", sizeof(webogenerating->dbgid)); + COM_AddWork(1, R_GenWorldEBO, webogenerating, NULL, 0, 0); + } + } + if (webostate) + { + entvis = surfvis = webostate->pvs; + + RSpeedEnd(RSPEED_WORLDNODE); + + CL_LinkStaticEntities(entvis); + TRACE(("dbg: calling R_DrawParticles\n")); + if (!r_refdef.recurse) + P_DrawParticles (); + + TRACE(("dbg: calling BE_DrawWorld\n")); + BE_DrawWorld(webostate->rbatches, surfvis); + + /*FIXME: move this away*/ + if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife) + Surf_LessenStains(); + return; + } + } +#endif + + + Surf_PushChains(cl.worldmodel->batches); #ifdef Q2BSPS if (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3) @@ -2380,7 +2963,7 @@ void Surf_DrawWorld (void) // entvis = surfvis = R_MarkLeafSurfaces_Q1(); // else { - entvis = R_MarkLeaves_Q1 (); + entvis = R_MarkLeaves_Q1 (false); if (!(r_novis.ival & 2)) VectorCopy (r_origin, modelorg); @@ -2395,6 +2978,8 @@ void Surf_DrawWorld (void) } } + RSpeedEnd(RSPEED_WORLDNODE); + if (!(r_refdef.flags & RDF_NOWORLDMODEL)) { CL_LinkStaticEntities(entvis); @@ -2403,15 +2988,14 @@ void Surf_DrawWorld (void) P_DrawParticles (); } - RSpeedEnd(RSPEED_WORLDNODE); TRACE(("dbg: calling BE_DrawWorld\n")); - BE_DrawWorld(true, surfvis); + BE_DrawWorld(cl.worldmodel->batches, surfvis); + + Surf_PopChains(cl.worldmodel->batches); /*FIXME: move this away*/ if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife) Surf_LessenStains(); - - Surf_PopChains(cl.worldmodel); } } @@ -2433,6 +3017,13 @@ void Surf_DeInit(void) { int i; +#ifdef THREADEDWORLD + while(webogenerating) + COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true); + R_DestroyWorldEBO(webostate); + webostate = NULL; +#endif + for (i = 0; i < numlightmaps; i++) { if (!lightmap[i]) @@ -2459,6 +3050,14 @@ void Surf_Clear(model_t *mod) vbo_t *vbo; if (mod->fromgame == fg_doom3) return;/*they're on the hunk*/ + +#ifdef THREADEDWORLD + while(webogenerating) + COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true); + R_DestroyWorldEBO(webostate); + webostate = NULL; +#endif + while(mod->vbos) { vbo = mod->vbos; @@ -2580,8 +3179,8 @@ int Surf_NewLightmaps(int count, int width, int height, qboolean deluxe) lightmap[i]->rectchange.l = 0; lightmap[i]->rectchange.t = 0; - lightmap[i]->rectchange.h = lightmap[i]->height; - lightmap[i]->rectchange.w = lightmap[i]->width; + lightmap[i]->rectchange.b = lightmap[i]->height; + lightmap[i]->rectchange.r = lightmap[i]->width; lightmap[i]->lightmap_texture = r_nulltex; @@ -2849,6 +3448,13 @@ void Surf_BuildModelLightmaps (model_t *m) void Surf_ClearLightmaps(void) { lightmap_bytes = 0; + +#ifdef THREADEDWORLD + while(webogenerating) + COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true); + R_DestroyWorldEBO(webostate); + webostate = NULL; +#endif } /* diff --git a/engine/client/render.h b/engine/client/render.h index 3f8cd49d1..a81f24c1a 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -302,7 +302,7 @@ void Surf_RenderAmbientLightmaps (struct msurface_s *fa, int ambient); int Surf_LightmapShift (struct model_s *model); #define LMBLOCK_SIZE_MAX 2048 //single axis typedef struct glRect_s { - unsigned short l,t,w,h; + unsigned short l,t,r,b; } glRect_t; typedef unsigned char stmap; struct mesh_s; @@ -488,7 +488,7 @@ double Media_TweekCaptureFrameTime(double oldtime, double time); void MYgluPerspective(double fovx, double fovy, double zNear, double zFar); void R_PushDlights (void); -qbyte *R_MarkLeaves_Q1 (void); +qbyte *R_MarkLeaves_Q1 (qboolean getvisonly); qbyte *R_CalcVis_Q1 (void); qbyte *R_MarkLeaves_Q2 (void); qbyte *R_MarkLeaves_Q3 (void); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index d18797fbd..883b3823c 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -112,7 +112,7 @@ cvar_t r_skin_overlays = SCVARF ("r_skin_overlays", "1", cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends."); -cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "1", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: BSP occlusion only.\n1: non-bsp occlusion also"); +cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: No occlusion, at all.\n1: BSP occlusion only (simple tracelines).\n2: non-bsp occlusion also (complex tracelines).\n3: Depthbuffer reads (forces synchronisation).\n4: occlusion queries."); cvar_t r_flashblend = SCVARF ("gl_flashblend", "0", CVAR_ARCHIVE); cvar_t r_flashblendscale = SCVARF ("gl_flashblendscale", "0.35", @@ -398,7 +398,7 @@ cvar_t r_fastturbcolour = CVARFD ("r_fastturbcolour", "0.1 0.2 0.3", CVAR_A cvar_t r_waterstyle = CVARFD ("r_waterstyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "Changes how water, and teleporters are drawn. Possible values are:\n0: fastturb-style block colour.\n1: regular q1-style water.\n2: refraction(ripply and transparent)\n3: refraction with reflection at an angle\n4: ripplemapped without reflections (requires particle effects)\n5: ripples+reflections"); cvar_t r_slimestyle = CVARFD ("r_slimestyle", "", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only slime. If empty, defers to r_waterstyle."); cvar_t r_lavastyle = CVARFD ("r_lavastyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle."); -cvar_t r_telestyle = CVARFD ("r_telestyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle."); +cvar_t r_telestyle = CVARFD ("r_telestyle", "1", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, "See r_waterstyle, but affects only teleporters. If empty, defers to r_waterstyle."); cvar_t r_vertexdlights = CVARD ("r_vertexdlights", "0", "Determine model lighting with respect to nearby dlights. Poor-man's rtlights."); @@ -2209,7 +2209,7 @@ qbyte *R_CalcVis_Q1 (void) } #endif -qbyte *R_MarkLeaves_Q1 (void) +qbyte *R_MarkLeaves_Q1 (qboolean getvisonly) { static qbyte *cvis[R_MAX_RECURSE]; qbyte *vis; @@ -2271,7 +2271,9 @@ qbyte *R_MarkLeaves_Q1 (void) r_visframecount++; - if (r_viewleaf && r_viewleaf->contents == Q1CONTENTS_SOLID) + if (getvisonly) + return vis; + else if (r_viewleaf && r_viewleaf->contents == Q1CONTENTS_SOLID) { //to improve spectating, when the camera is in a wall, we ignore any sky leafs. //this prevents seeing the upwards-facing sky surfaces within the sky volumes. diff --git a/engine/client/sbar.c b/engine/client/sbar.c index bc4825589..f7bec943d 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -2165,7 +2165,7 @@ void Sbar_DrawScoreboard (void) for (pnum = 0; pnum < cl.splitclients; pnum++) { - if (cl.spectator) + if (cl.spectator && (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)) { int t = cl.playerview[pnum].cam_spec_track; if (t < 0 || !CAM_ISLOCKED(&cl.playerview[pnum])) diff --git a/engine/client/snd_xaudio.c b/engine/client/snd_xaudio.c new file mode 100644 index 000000000..ebc44abae --- /dev/null +++ b/engine/client/snd_xaudio.c @@ -0,0 +1,256 @@ +#include "quakedef.h" + +//frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved. +//I suppose it has a use with WINRT... although that doesn't make it useful. +//we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies. + +#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY) +#include "winquake.h" +#include + +#define SDRVNAME "XAudio2" + +typedef struct +{ + IXAudio2VoiceCallback cb; //must be first. yay for fake single inheritance. + + IXAudio2 *ixa; + IXAudio2MasteringVoice *master; + IXAudio2SourceVoice *source; + + //contiguous block of memory, because its easier. + qbyte *bufferstart; + unsigned int subbuffersize; //in samplepairs + unsigned int buffercount; + unsigned int bufferidx; + unsigned int bufferavail; +} xaud_t; + +static void *XAUDIO_Lock(soundcardinfo_t *sc, unsigned int *startoffset) +{ + qbyte *ret; + xaud_t *xa = sc->handle; + ret = xa->bufferstart; + +// *startoffset = 0; +// ret += xa->subbuffersize * xa->bufferidx; + + return ret; +} +static void XAUDIO_Unlock(soundcardinfo_t *sc, void *buffer) +{ +} +static unsigned int XAUDIO_GetDMAPos(soundcardinfo_t *sc) +{ + xaud_t *xa = sc->handle; + unsigned int s = (xa->bufferidx+xa->bufferavail) * xa->subbuffersize * sc->sn.numchannels; + return s; +} +static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end) +{ + xaud_t *xa = sc->handle; + XAUDIO2_BUFFER buf; + + //determine total buffer size + int buffersize = sc->sn.samples*sc->sn.samplebits/8; + + //determine time offsets in bytes + start *= sc->sn.numchannels*sc->sn.samplebits/8; + end *= sc->sn.numchannels*sc->sn.samplebits/8; + + while (start < end) + { + if (!xa->bufferavail) + break; //o.O that's not meant to happen + memset(&buf, 0, sizeof(buf)); + buf.AudioBytes = end - start; + if (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8) + { + if (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8) + { //dma code should ensure that only multiples of 'samplequeue' are processed. + Con_Printf("XAudio2 underrun\n"); + break; + } + buf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8; + } + buf.pAudioData = xa->bufferstart + (start%buffersize); + if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize) + { //this shouldn't ever happen either + Con_Printf("XAudio2 overrun\n"); + break; + } + IXAudio2SourceVoice_SubmitSourceBuffer(xa->source, &buf, NULL); + xa->bufferidx += 1; + xa->bufferavail -= 1; + start += buf.AudioBytes; + } +} + +static void XAUDIO_Shutdown(soundcardinfo_t *sc) +{ + xaud_t *xa = sc->handle; + //releases are allowed to block, supposedly. + IXAudio2SourceVoice_DestroyVoice(xa->source); + IXAudio2MasteringVoice_DestroyVoice(xa->master); + IXAudio2_Release(xa->ixa); + BZ_Free(xa->bufferstart); + Z_Free(xa); + sc->handle = NULL; +} + +void WINAPI XAUDIO_CB_OnVoiceProcessingPassStart (IXAudio2VoiceCallback *ths, UINT32 BytesRequired) {} +void WINAPI XAUDIO_CB_OnVoiceProcessingPassEnd (IXAudio2VoiceCallback *ths) {} +void WINAPI XAUDIO_CB_OnStreamEnd (IXAudio2VoiceCallback *ths) {} +void WINAPI XAUDIO_CB_OnBufferStart (IXAudio2VoiceCallback *ths, void* pBufferContext) {} +void WINAPI XAUDIO_CB_OnBufferEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {xaud_t *xa = (xaud_t*)ths; S_LockMixer(); xa->bufferavail+=1; S_UnlockMixer();} +void WINAPI XAUDIO_CB_OnLoopEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {} +void WINAPI XAUDIO_CB_OnVoiceError (IXAudio2VoiceCallback *ths, void* pBufferContext, HRESULT Error) {} +static IXAudio2VoiceCallbackVtbl cbvtbl = +{ + XAUDIO_CB_OnVoiceProcessingPassStart, + XAUDIO_CB_OnVoiceProcessingPassEnd, + XAUDIO_CB_OnStreamEnd, + XAUDIO_CB_OnBufferStart, + XAUDIO_CB_OnBufferEnd, + XAUDIO_CB_OnLoopEnd, + XAUDIO_CB_OnVoiceError +}; + +static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname) +{ +#ifdef WINRT + char *dev = NULL; +#else + int dev = 0; +#endif + xaud_t *xa = Z_Malloc(sizeof(*xa)); + WAVEFORMATEX wfmt; + + xa->cb.lpVtbl = &cbvtbl; + + sc->sn.numchannels = 2; + + wfmt.wFormatTag = WAVE_FORMAT_PCM; + wfmt.nChannels = sc->sn.numchannels; + wfmt.nSamplesPerSec = sc->sn.speed; + wfmt.wBitsPerSample = sc->sn.samplebits; + wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8); + wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign; + wfmt.cbSize = 0; + + sc->inactive_sound = true; + xa->buffercount = xa->bufferavail = 3; //submit this many straight up + xa->subbuffersize = 256; //number of sampleblocks per submission + sc->samplequeue = -1; //-1 means we're streaming, XAUDIO_GetDMAPos returns exactly as much as we want to paint to. + + sc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels; + + xa->bufferstart = BZ_Malloc(sc->sn.samples * (sc->sn.samplebits/8)); + + if (xa->bufferstart) + { + if (SUCCEEDED(XAudio2Create(&xa->ixa, 0, XAUDIO2_DEFAULT_PROCESSOR))) + { +#ifdef WINRT + if (SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL, AudioCategory_GameEffects))) +#else + if (cardname && *cardname) + { + UINT32 devs = 0; + XAUDIO2_DEVICE_DETAILS details; + char id[MAX_QPATH]; + if (FAILED(IXAudio2_GetDeviceCount(xa->ixa, &devs))) + devs = 0; + + for (dev = 0; dev < devs; dev++) + { + if (SUCCEEDED(IXAudio2_GetDeviceDetails(xa->ixa, dev, &details))) + { + narrowen(id, sizeof(id), details.DeviceID); + + if (!strcmp(id, cardname)) + break; + } + } + if (dev == devs) + dev = ~0; //something invalid. + } + + /* + //FIXME: correct the details to match the hardware + */ + + + if (dev != ~0 && SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL))) +#endif + { + //egads + XAUDIO2_VOICE_SENDS vs; + XAUDIO2_SEND_DESCRIPTOR sd[1]; + vs.SendCount = 1; + vs.pSends = sd; + sd[0].Flags = 0; + sd[0].pOutputVoice = (IXAudio2Voice*)xa->master; + + if (SUCCEEDED(IXAudio2_CreateSourceVoice(xa->ixa, &xa->source, &wfmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &xa->cb, &vs, NULL))) + { + sc->handle = xa; + sc->GetDMAPos = XAUDIO_GetDMAPos; + sc->Lock = XAUDIO_Lock; + sc->Unlock = XAUDIO_Unlock; + sc->Submit = XAUDIO_Submit; + sc->Shutdown = XAUDIO_Shutdown; + + IXAudio2SourceVoice_Start(xa->source, 0, XAUDIO2_COMMIT_NOW); + return true; + } + IXAudio2MasteringVoice_DestroyVoice(xa->master); + } + IXAudio2_Release(xa->ixa); + } + BZ_Free(xa->bufferstart); + } + Z_Free(xa); + return false; +} + +qboolean QDECL XAUDIO_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename)) +{ + IXAudio2 *ixa; +#ifndef WINRT + CoInitializeEx(NULL, COINIT_MULTITHREADED); + //winrt provides no enumeration mechanism. + if (SUCCEEDED(XAudio2Create(&ixa, 0, XAUDIO2_DEFAULT_PROCESSOR))) + { + UINT32 devs = 0, i; + XAUDIO2_DEVICE_DETAILS details; + char id[MAX_QPATH], name[MAX_QPATH]; + if (FAILED(IXAudio2_GetDeviceCount(ixa, &devs))) + devs = 0; + + strcpy(name, "XA2:"); + + for (i = 0; i < devs; i++) + { + if (SUCCEEDED(IXAudio2_GetDeviceDetails(ixa, i, &details))) + { + narrowen(id, sizeof(id), details.DeviceID); + narrowen(name+4, sizeof(name)-4, details.DisplayName); + + callback(SDRVNAME, id, name); + } + } + IXAudio2_Release(ixa); + return true; + } +#endif + return false; +} + +sounddriver_t XAUDIO2_Output = +{ + SDRVNAME, + XAUDIO_InitCard, + XAUDIO_Enumerate +}; +#endif diff --git a/engine/client/vid_headless.c b/engine/client/vid_headless.c index f4c355724..31227342e 100644 --- a/engine/client/vid_headless.c +++ b/engine/client/vid_headless.c @@ -25,12 +25,6 @@ static void Headless_IMG_UpdateFiltering (image_t *imagelist, int filtermip[3], } static qboolean Headless_IMG_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips) { - int i; - for (i = 0; i < mips->mipcount; i++) - if (mips->mip[i].needfree) - Z_Free(mips->mip[i].data); - if (mips->extrafree) - Z_Free(mips->extrafree); return true; } static void Headless_IMG_DestroyTexture (texid_t tex) @@ -141,6 +135,8 @@ static qboolean Headless_VID_Init (rendererstate_t *info, unsigned char *pale Shell_NotifyIconA(NIM_ADD, &data); } #endif + + memset(&sh_config, 0, sizeof(sh_config)); return true; } static void Headless_VID_DeInit (void) @@ -196,7 +192,7 @@ static struct batch_s *Headless_BE_GetTempBatch (void) { return NULL; } -static void Headless_BE_DrawWorld (qboolean drawworld, qbyte *vis) +static void Headless_BE_DrawWorld (struct batch_s **worldbatches, qbyte *vis) { } static void Headless_BE_Init (void) diff --git a/engine/client/wad.c b/engine/client/wad.c index 05637ef9c..beafb7b59 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -244,6 +244,14 @@ void SwapPic (qpic_t *pic) //FIXME: convert to linked list. is hunk possible? //hash tables? #define TEXWAD_MAXIMAGES 16384 + +typedef struct wadfile_s +{ + vfsfile_t *file; + struct wadfile_s *next; + char name[1]; +} wadfile_t; + typedef struct { char name[16]; @@ -253,18 +261,15 @@ typedef struct } texwadlump_t; int numwadtextures; static texwadlump_t texwadlump[TEXWAD_MAXIMAGES]; - -typedef struct wadfile_s { - vfsfile_t *file; - struct wadfile_s *next; - char name[1]; -} wadfile_t; +void *wadmutex; wadfile_t *openwadfiles; void Wads_Flush (void) { wadfile_t *wf; + if (wadmutex) + Sys_LockMutex(wadmutex); while(openwadfiles) { VFS_CLOSE(openwadfiles->file); @@ -275,6 +280,8 @@ void Wads_Flush (void) } numwadtextures=0; + if (wadmutex) + Sys_UnlockMutex(wadmutex); } /* ==================== @@ -411,12 +418,23 @@ qbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *hei tex->offsets[1] == tex->offsets[0] + (tex->width)*(tex->height) && tex->offsets[2] == tex->offsets[1] + (tex->width>>1)*(tex->height>>1) && tex->offsets[3] == tex->offsets[2] + (tex->width>>2)*(tex->height>>2) && - lumpsize == tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2 + 768) + ((lumpsize+3)&~3) >= ((tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2 + 768+3)&~3)) pal = (qbyte *)tex + tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2; else pal = host_basepal; - for (d = 0;d < tex->width * tex->height;d++) + if (tex->offsets[0] + tex->width * tex->height > lumpsize) + { //fucked texture. + for (d = 0;d < tex->width * tex->height;d++) + { + out[0] = 0; + out[1] = 255; + out[2] = 0; + out[3] = 255; + out += 4; + } + } + else for (d = 0;d < tex->width * tex->height;d++) { p = *in++; if (alpha==1 && p == 255) //only allow alpha on '{' textures @@ -473,30 +491,34 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp texname[16] = 0; W_CleanupName (name, texname); + Sys_LockMutex(wadmutex); for (i = 0;i < numwadtextures;i++) { if (!strcmp(texname, texwadlump[i].name)) // found it { file = texwadlump[i].file; - if (!VFS_SEEK(file, texwadlump[i].position)) - {Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;} - tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like - if (!tex) - return NULL; - if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size) - {Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;} + if (VFS_SEEK(file, texwadlump[i].position)) + { + tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like + if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size) + { + Sys_UnlockMutex(wadmutex); + tex->width = LittleLong(tex->width); + tex->height = LittleLong(tex->height); + for (j = 0;j < MIPLEVELS;j++) + tex->offsets[j] = LittleLong(tex->offsets[j]); - tex->width = LittleLong(tex->width); - tex->height = LittleLong(tex->height); - for (j = 0;j < MIPLEVELS;j++) - tex->offsets[j] = LittleLong(tex->offsets[j]); - - data = W_ConvertWAD3Texture(tex, texwadlump[i].size, width, height, usesalpha); - BZ_Free(tex); - return data; + data = W_ConvertWAD3Texture(tex, texwadlump[i].size, width, height, usesalpha); + BZ_Free(tex); + return data; + } + } + Con_Printf("W_GetTexture: corrupt WAD3 file\n"); + break; } } + Sys_UnlockMutex(wadmutex); return NULL; } @@ -509,27 +531,30 @@ miptex_t *W_GetMipTex(const char *name) texname[16] = 0; W_CleanupName (name, texname); + Sys_LockMutex(wadmutex); for (i = 0;i < numwadtextures;i++) { if (!strcmp(texname, texwadlump[i].name)) // found it { file = texwadlump[i].file; - if (!VFS_SEEK(file, texwadlump[i].position)) - {Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;} - - tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like - if (!tex) - return NULL; - if (VFS_READ(file, tex, texwadlump[i].size) < texwadlump[i].size) - {Con_Printf("W_GetTexture: corrupt WAD3 file");return NULL;} - - tex->width = LittleLong(tex->width); - tex->height = LittleLong(tex->height); - for (j = 0;j < MIPLEVELS;j++) - tex->offsets[j] = LittleLong(tex->offsets[j]); - return tex; + if (VFS_SEEK(file, texwadlump[i].position)) + { + tex = BZ_Malloc(texwadlump[i].size); //temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like + if (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size) + { + Sys_UnlockMutex(wadmutex); + tex->width = LittleLong(tex->width); + tex->height = LittleLong(tex->height); + for (j = 0;j < MIPLEVELS;j++) + tex->offsets[j] = LittleLong(tex->offsets[j]); + return tex; + } + } + Con_Printf("W_GetTexture: corrupt WAD3 file\n"); + break; } } + Sys_UnlockMutex(wadmutex); return NULL; } @@ -689,12 +714,14 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in key[3] = ' '; Q_strncpyz(key+4, token, sizeof(key)-4); Cbuf_AddText(key, RESTRICT_INSECURE); + Cbuf_AddText("\n", RESTRICT_INSECURE); } else if (!strcmp("waterfog", key)) //q1 extension. FIXME: should be made temporary. { memcpy(key, "waterfog ", 9); Q_strncpyz(key+9, token, sizeof(key)-9); Cbuf_AddText(key, RESTRICT_INSECURE); + Cbuf_AddText("\n", RESTRICT_INSECURE); } else if (!strncmp("cvar_", key, 5)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines). { diff --git a/engine/client/wad.h b/engine/client/wad.h index dc172c544..fbb6d497d 100644 --- a/engine/client/wad.h +++ b/engine/client/wad.h @@ -93,6 +93,7 @@ void *W_GetLumpName (char *name); void *W_SafeGetLumpName (const char *name); void *W_GetLumpNum (int num); void Wads_Flush (void); +extern void *wadmutex; void SwapPic (qpic_t *pic); diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 88faccb76..3953e1418 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -3546,7 +3546,7 @@ qbool TP_CheckSoundTrigger (char *str) COM_DefaultExtension (soundname, ".wav", sizeof(soundname)); // make sure we have it on disk (FIXME) - if (!FS_FLocateFile (va("sound/%s", soundname), FSLFRT_IFFOUND, NULL)) + if (!FS_FLocateFile (va("sound/%s", soundname), FSLF_IFFOUND, NULL)) return false; // now play the sound diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index c0e8501e7..21085938d 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -179,7 +179,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef AVAIL_JPEGLIB #undef AVAIL_XZDEC -#if defined(_WIN32) && !defined(FTE_SDL) !defined(MULTITHREAD) //always thread on win32 non-minimal builds +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(MULTITHREAD) //always thread on win32 non-minimal builds #define MULTITHREAD #endif #elif defined(MINIMAL) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index b461ff94e..266952aff 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -616,7 +616,7 @@ void Cmd_Exec_f (void) else Q_strncpyz(name, Cmd_Argv(1), sizeof(name)); - if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLFRT_IFFOUND, &loc)) + if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLF_IFFOUND, &loc)) { Con_TPrintf ("couldn't exec %s\n", name); return; diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index bcda392d3..fcb6dbc9c 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -984,7 +984,7 @@ void R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int } } - if (r_vertexdlights.ival && r_dynamic.ival) + if (r_vertexdlights.ival && r_dynamic.ival > 0) { unsigned int lno, v; vec3_t dir, rel; @@ -2787,13 +2787,11 @@ void Mod_LoadAliasShaders(model_t *mod) //Q1 model loading #if 1 -static galiasinfo_t *galias; -static dmdl_t *pq1inmodel; #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; // mdltype 0 = q1, 1 = qtest, 2 = rapo/h2 -static void Alias_LoadPose(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) +static void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) { int j; #ifdef HEXEN2 @@ -2830,7 +2828,7 @@ static void Alias_LoadPose(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t } } } -static void Alias_LoadPose16(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) +static void Q1MDL_LoadPose16(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype) { //quakeforge's MD16 format has regular 8bit stuff, trailed by an extra low-order set of the verts providing the extra 8bits of precision. //its worth noting that the model could be rendered using the high-order parts only, if your software renderer only supports that or whatever. @@ -2855,7 +2853,7 @@ static void Alias_LoadPose16(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_ } } } -static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype) +static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype) { galiaspose_t *pose; galiasanimation_t *frame = galias->ofsanimations; @@ -2910,12 +2908,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe if (mdltype & 16) { - Alias_LoadPose16(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); + Q1MDL_LoadPose16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype); pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts*2]; } else { - Alias_LoadPose(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); + Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype); pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts]; } @@ -2972,12 +2970,12 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe if (mdltype & 16) { - Alias_LoadPose16(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); + Q1MDL_LoadPose16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype); pinframe += pq1inmodel->numverts*2; } else { - Alias_LoadPose(verts, normals, svec, tvec, pinframe, seamremaps, mdltype); + Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype); pinframe += pq1inmodel->numverts; } @@ -3007,7 +3005,7 @@ static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframe //greatly reduced version of Q1_LoadSkins //just skips over the data -static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintranstype) +static void *Q1MDL_LoadSkins_SV (galiasinfo_t *galias, dmdl_t *pq1inmodel, daliasskintype_t *pskintype, unsigned int skintranstype) { int i; int s; @@ -3038,7 +3036,7 @@ static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintran } #ifndef SERVERONLY -static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype) +static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype) { skinframe_t *frames; char skinname[MAX_QPATH]; @@ -3362,8 +3360,8 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) dh2triangle_t *pinh2triangles; qboolean rapo = false; #endif - - pq1inmodel = (dmdl_t *)buffer; + galiasinfo_t *galias; + dmdl_t *pq1inmodel = (dmdl_t *)buffer; hdrsize = sizeof(dmdl_t) - sizeof(int); @@ -3450,11 +3448,11 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) { default: #ifndef SERVERONLY - pinstverts = (dstvert_t *)Q1_LoadSkins_GL(mod, skinstart, skintranstype); + pinstverts = (dstvert_t *)Q1MDL_LoadSkins_GL(galias, pq1inmodel, mod, skinstart, skintranstype); break; #endif case QR_NONE: - pinstverts = (dstvert_t *)Q1_LoadSkins_SV(skinstart, skintranstype); + pinstverts = (dstvert_t *)Q1MDL_LoadSkins_SV(galias, pq1inmodel, skinstart, skintranstype); break; } @@ -3535,7 +3533,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) #endif end = &pinh2triangles[pq1inmodel->numtris]; - if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, 2) == NULL) + if (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, 2) == NULL) { BZ_Free(seamremap); ZG_FreeGroup(&mod->memgroup); @@ -3612,7 +3610,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) end = &pinq1triangles[pq1inmodel->numtris]; //frames - if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, (sixteenbit?16:0) | (qtest?1:0)) == NULL) + if (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, (sixteenbit?16:0) | (qtest?1:0)) == NULL) { BZ_Free(seamremap); ZG_FreeGroup(&mod->memgroup); @@ -3706,7 +3704,7 @@ typedef struct #define Q2NUMVERTEXNORMALS 162 extern vec3_t bytedirs[Q2NUMVERTEXNORMALS]; -static void Q2_LoadSkins(model_t *mod, md2_t *pq2inmodel, char *skins) +static void Q2MD2_LoadSkins(galiasinfo_t *galias, model_t *mod, md2_t *pq2inmodel, char *skins) { #ifndef SERVERONLY int i; @@ -3777,6 +3775,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) int numverts; int size; + galiasinfo_t *galias; mod->engineflags |= MDLF_NEEDOVERBRIGHT; @@ -3820,7 +3819,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) galias->nextsurf = 0; //skins - Q2_LoadSkins(mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins))); + Q2MD2_LoadSkins(galias, mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins))); //trianglelists; pintri = (dmd2triangle_t *)((char *)pq2inmodel + LittleLong(pq2inmodel->ofs_tris)); @@ -4604,6 +4603,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) md3Header_t *header; md3Surface_t *surf; + galiasinfo_t *galias; header = buffer; diff --git a/engine/common/common.c b/engine/common/common.c index 6b9a34021..c0e446460 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -4724,11 +4724,24 @@ void COM_ErrorMe_f(void) #ifdef LOADERTHREAD -#define WORKERTHREADS (1+1+4) +#define WG_MAIN 0 +#define WG_LOADER 1 +#define WG_COUNT 2 //main and loaders +#define WORKERTHREADS 16 //max /*multithreading worker thread stuff*/ -static void *com_workercondition[WORKERTHREADS]; -static qboolean com_workerdone[WORKERTHREADS]; -static void *com_workerthread[WORKERTHREADS]; +static int com_liveworkers[WG_COUNT]; +static void *com_workercondition[WG_COUNT]; +static volatile int com_workeracksequence; +static struct com_worker_s +{ + void *thread; + volatile enum { + WR_NONE, + WR_DIE, + WR_ACK //updates ackseq to com_workeracksequence and sends a signal to WG_MAIN + } request; + volatile int ackseq; +} com_worker[WORKERTHREADS]; static unsigned int mainthreadid; qboolean com_workererror; static struct com_work_s @@ -4739,77 +4752,12 @@ static struct com_work_s void *data; size_t a; size_t b; -} *com_work_head[WORKERTHREADS], *com_work_tail[WORKERTHREADS]; -static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b) -{ - Sys_Error(data); -} -void COM_WorkerAbort(char *message) -{ - int us; - struct com_work_s work; - if (Sys_IsMainThread()) - return; - com_workererror = true; - - if (!com_workercondition[0]) - return; //Sys_IsMainThread was probably called too early... - - memset(&work, 0, sizeof(work)); - work.func = Sys_ErrorThread; - work.ctx = NULL; - work.data = message; - work.a = 0; - work.b = 0; - - Sys_LockConditional(com_workercondition[0]); - if (com_work_tail[0]) - { - com_work_tail[0]->next = &work; - com_work_tail[0] = &work; - } - else - com_work_head[0] = com_work_tail[0] = &work; - Sys_ConditionSignal(com_workercondition[0]); - Sys_UnlockConditional(com_workercondition[0]); - - //find out which worker we are - for (us = WORKERTHREADS-1; us > 0; us--) - if (Sys_IsThread(com_workerthread[us])) - break; - if (us) - { - //and post any pending work we have back over to the main thread, because we're going down as soon as we can. - while(!com_workerdone[us]) - { - struct com_work_s *w; - Sys_LockConditional(com_workercondition[us]); - w = com_work_head[us]; - if (w) - com_work_head[us] = w->next; - if (!com_work_head[us]) - com_work_head[us] = com_work_tail[us] = NULL; - Sys_UnlockConditional(com_workercondition[us]); - if (w) - { - COM_AddWork(0, w->func, w->ctx, w->data, w->a, w->b); - Z_Free(w); - } - else - Sys_ConditionSignal(com_workercondition[0]); - - Sys_Sleep(0.1); - } - com_workerdone[0] = true; - Sys_ConditionSignal(com_workercondition[0]); - } - Sys_ThreadAbort(); -} +} *com_work_head[WG_COUNT], *com_work_tail[WG_COUNT]; //return if there's *any* loading that needs to be done anywhere. qboolean COM_HasWork(void) { unsigned int i; - for (i = 0; i < WORKERTHREADS-1; i++) + for (i = 0; i < WG_COUNT; i++) { if (com_work_head[i]) return true; @@ -4817,12 +4765,15 @@ qboolean COM_HasWork(void) return false; } //thread==0 is main thread, thread==1 is a worker thread -void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) +void COM_AddWork(int tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) { struct com_work_s *work; + if (tg >= WG_COUNT) + return; + //no worker there, just do it immediately on this thread instead of pushing it to the worker. - if (thread && (!com_workerthread[thread] || com_workererror)) + if (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror)) { func(ctx, data, a, b); return; @@ -4837,19 +4788,19 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t work->b = b; //queue it (fifo) - Sys_LockConditional(com_workercondition[thread]); - if (com_work_tail[thread]) + Sys_LockConditional(com_workercondition[tg]); + if (com_work_tail[tg]) { - com_work_tail[thread]->next = work; - com_work_tail[thread] = work; + com_work_tail[tg]->next = work; + com_work_tail[tg] = work; } else - com_work_head[thread] = com_work_tail[thread] = work; + com_work_head[tg] = com_work_tail[tg] = work; // Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); - Sys_ConditionSignal(com_workercondition[thread]); - Sys_UnlockConditional(com_workercondition[thread]); + Sys_ConditionSignal(com_workercondition[tg]); + Sys_UnlockConditional(com_workercondition[tg]); // if (!com_workerthread[thread]) // while(COM_DoWork(thread, false)) @@ -4857,72 +4808,157 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t } //leavelocked = false == poll mode. //leavelocked = true == safe sleeping -qboolean COM_DoWork(int thread, qboolean leavelocked) +qboolean COM_DoWork(int tg, qboolean leavelocked) { struct com_work_s *work; + if (tg >= WG_COUNT) + return false; if (!leavelocked) { //skip the locks if it looks like we can be lazy. - if (!com_work_head[thread]) + if (!com_work_head[tg]) return false; - Sys_LockConditional(com_workercondition[thread]); + Sys_LockConditional(com_workercondition[tg]); } - work = com_work_head[thread]; + work = com_work_head[tg]; if (work) - com_work_head[thread] = work->next; - if (!com_work_head[thread]) - com_work_head[thread] = com_work_tail[thread] = NULL; + com_work_head[tg] = work->next; + if (!com_work_head[tg]) + com_work_head[tg] = com_work_tail[tg] = NULL; if (work) { // Sys_Printf("%x: Doing work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); - Sys_UnlockConditional(com_workercondition[thread]); + Sys_UnlockConditional(com_workercondition[tg]); work->func(work->ctx, work->data, work->a, work->b); Z_Free(work); if (leavelocked) - Sys_LockConditional(com_workercondition[thread]); + Sys_LockConditional(com_workercondition[tg]); return true; //did something, check again } if (!leavelocked) - Sys_UnlockConditional(com_workercondition[thread]); + Sys_UnlockConditional(com_workercondition[tg]); //nothing going on, if leavelocked then noone can add anything until we sleep. return false; } -static void COM_WorkerSync_StopWorker(void *ctx, void *data, size_t a, size_t b) +static void COM_WorkerSync_ThreadAck(void *ctx, void *data, size_t a, size_t b) { - com_workerdone[a] = true; + int us; + int *ackbuf = ctx; + + Sys_LockConditional(com_workercondition[WG_MAIN]); + //find out which worker we are, and flag ourselves as having acked the main thread to clean us up + for (us = 0; us < WORKERTHREADS; us++) + { + if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread)) + { + ackbuf[us] = true; + break; + } + } + *(int*)data += 1; + //and tell the main thread it can stop being idle now + Sys_ConditionSignal(com_workercondition[WG_MAIN]); + Sys_UnlockConditional(com_workercondition[WG_MAIN]); } -static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b) +/*static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b) { Sys_LockConditional(com_workercondition[a]); com_workerdone[a] = true; Sys_ConditionSignal(com_workercondition[a]); Sys_UnlockConditional(com_workercondition[a]); +}*/ +static void COM_WorkerSync_WorkerStopped(void *ctx, void *data, size_t a, size_t b) +{ + struct com_worker_s *thread = ctx; + if (thread->thread) + { + //the worker signaled us then stopped looping + Sys_WaitOnThread(thread->thread); + thread->thread = NULL; + + Sys_LockConditional(com_workercondition[b]); + com_liveworkers[b] -= 1; + Sys_UnlockConditional(com_workercondition[b]); + } + else + Con_Printf("worker thread died twice?\n"); + + //if that was the last thread, make sure any work pending for that group is completed. + if (!com_liveworkers[b]) + { + while(COM_DoWork(b, false)) + ; + } } static int COM_WorkerThread(void *arg) { - int thread = (void**)arg - com_workerthread; - Sys_LockConditional(com_workercondition[thread]); - do + struct com_worker_s *thread = arg; + int group = WG_LOADER; + Sys_LockConditional(com_workercondition[group]); + com_liveworkers[group]++; + for(;;) { - while(COM_DoWork(thread, true)) + while(COM_DoWork(group, true)) ; - if (com_workerdone[thread]) + if (thread->request) //flagged from some work + { + if (thread->request == WR_DIE) + break; + if (thread->request == WR_ACK) + { + thread->request = WR_NONE; + thread->ackseq = com_workeracksequence; + Sys_UnlockConditional(com_workercondition[group]); + Sys_ConditionBroadcast(com_workercondition[WG_MAIN]); //try to wake up whoever wanted us to ack them + Sys_LockConditional(com_workercondition[group]); + continue; + } + } + else if (!Sys_ConditionWait(com_workercondition[group])) break; - } while (Sys_ConditionWait(com_workercondition[thread])); - Sys_UnlockConditional(com_workercondition[thread]); + } + Sys_UnlockConditional(com_workercondition[group]); - //no more work please... - *(void**)arg = NULL; - //and wake up main thread - COM_WorkerSync_SignalMain(NULL, NULL, 0, 0); + //and wake up main thread to clean up our handle + COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, thread, NULL, 0, group); return 0; } +static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b) +{ + //posted to main thread from a worker. + Sys_Error(data); +} +void COM_WorkerAbort(char *message) +{ + int group = -1; + int us; + if (Sys_IsMainThread()) + return; + com_workererror = true; + + if (!com_workercondition[WG_MAIN]) + return; //Sys_IsMainThread was probably called too early... + + //find out which worker we are, and tell the main thread to clean us up + for (us = 0; us < WORKERTHREADS; us++) + if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread)) + { + group = WG_LOADER; + COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, &com_worker[us], NULL, 0, group); + break; + } + + //now tell the main thread that it should be crashing, and why. + COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0); + + Sys_ThreadAbort(); +} #ifndef COM_AssertMainThread void COM_AssertMainThread(const char *msg) @@ -4936,49 +4972,30 @@ void COM_AssertMainThread(const char *msg) void COM_DestroyWorkerThread(void) { int i; - COM_WorkerFullSync(); + if (!com_resourcemutex) + return; // com_workererror = false; + Sys_LockConditional(com_workercondition[WG_LOADER]); for (i = 0; i < WORKERTHREADS; i++) - { - if (com_workerthread[i]) - { - void *thread = com_workerthread[i]; - com_workerdone[0] = false; - //send it the terminate message - COM_AddWork(i, COM_WorkerSync_StopWorker, NULL, NULL, i, 0); + com_worker[i].request = WR_DIE; //flag them all to die + Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up + Sys_UnlockConditional(com_workercondition[WG_LOADER]); - //wait for the response while servicing anything that it might be waiting for. - Sys_LockConditional(com_workercondition[0]); - do - { - if (com_workererror) - break; - while(COM_DoWork(0, true)) - ; - if (com_workerdone[0]) - break; - } while (Sys_ConditionWait(com_workercondition[0])); - Sys_UnlockConditional(com_workercondition[0]); + while(COM_DoWork(WG_LOADER, false)) //finish any work that got posted to it that it neglected to finish. + ; + while(COM_DoWork(WG_MAIN, false)) + ; - //and now that we know its going down and will not wait any more, we can block for its final moments - Sys_WaitOnThread(thread); + COM_WorkerFullSync(); - //finish any work that got posted to it that it neglected to finish. - while(COM_DoWork(i, true)) - ; - } - } - - for (i = 0; i < WORKERTHREADS; i++) + for (i = 0; i < WG_COUNT; i++) { if (com_workercondition[i]) Sys_DestroyConditional(com_workercondition[i]); com_workercondition[i] = NULL; - com_workerthread[i] = NULL; } - if (com_resourcemutex) - Sys_DestroyMutex(com_resourcemutex); + Sys_DestroyMutex(com_resourcemutex); com_resourcemutex = NULL; } @@ -4988,36 +5005,56 @@ void COM_WorkerFullSync(void) qboolean repeat; int i; - for (i = 1; i < WORKERTHREADS; i++) - { - if (!com_workerthread[i]) - continue; + while(COM_DoWork(WG_MAIN, false)) + ; - //main thread asks worker thread to set main thread's 'done' flag. - //the worker might be posting work to the main thread and back (shaders with texures) so make sure that the only work we do before the reply is the reply itself. - do + if (!com_liveworkers[WG_LOADER]) + return; + + com_workeracksequence++; + + Sys_LockConditional(com_workercondition[WG_MAIN]); + do + { + if (!COM_HasWork()) { - int cmds = 0; - com_workerdone[0] = false; - repeat = COM_HasWork(); - COM_AddWork(i, COM_WorkerSync_SignalMain, NULL, NULL, 0, 0); - Sys_LockConditional(com_workercondition[0]); - do + Sys_UnlockConditional(com_workercondition[WG_MAIN]); + Sys_LockConditional(com_workercondition[WG_LOADER]); + repeat = false; + for (i = 0; i < WORKERTHREADS; i++) { - if (com_workererror) - break; - while(COM_DoWork(0, true)) - cmds++; - if (com_workerdone[0]) - break; - } while (Sys_ConditionWait(com_workercondition[0])); - Sys_UnlockConditional(com_workercondition[0]); - if (com_workererror) - break; - if (cmds > 1) - repeat = true; - } while (COM_DoWork(0, false) || repeat); //outer loop ensures there isn't anything pingponging between - } + if (com_worker[i].ackseq != com_workeracksequence && com_worker[i].request == WR_NONE) + { + com_worker[i].request = WR_ACK; + repeat = true; + } + } + if (repeat) //we're unable to signal a specific thread due to only having one condition. oh well. WAKE UP GUYS! + Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); + Sys_UnlockConditional(com_workercondition[WG_LOADER]); + Sys_LockConditional(com_workercondition[WG_MAIN]); + } + + repeat = COM_DoWork(WG_MAIN, true); + + if (repeat) + { //if we just did something, we may have posted something new to a worker... bum. + com_workeracksequence++; + } + else + { + for (i = 0; i < WORKERTHREADS; i++) + { + if (com_worker[i].thread && com_worker[i].ackseq != com_workeracksequence) + repeat = true; + } + if (repeat) + Sys_ConditionWait(com_workercondition[WG_MAIN]); + } + if (com_workererror) + break; + } while(repeat); + Sys_UnlockConditional(com_workercondition[WG_MAIN]); } //main thread wants a specific object to be prioritised. @@ -5032,18 +5069,20 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value) // Con_Printf("waiting for %p %s\n", priorityctx, priorityctx); + COM_DoWork(WG_MAIN, false); + //boost the priority of the object that we're waiting for on the other thread, if we can find it. //this avoids waiting for everything. //if we can't find it, then its probably currently being processed anyway. //main thread is meant to do all loadstate value changes anyway, ensuring that we're woken up properly in this case. if (priorityctx) { - unsigned int thread; + unsigned int grp; qboolean found = false; - for (thread = 1; thread < WORKERTHREADS && !found; thread++) + for (grp = WG_LOADER; grp < WG_MAIN && !found; grp++) { - Sys_LockConditional(com_workercondition[thread]); - for (link = &com_work_head[thread], work = NULL; *link; link = &(*link)->next) + Sys_LockConditional(com_workercondition[grp]); + for (link = &com_work_head[grp], work = NULL; *link; link = &(*link)->next) { prev = work; work = *link; @@ -5052,30 +5091,30 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value) *link = work->next; if (!work->next) - com_work_tail[thread] = prev; + com_work_tail[grp] = prev; //link it in at the head, so its the next thing seen. - work->next = com_work_head[thread]; - com_work_head[thread] = work; + work->next = com_work_head[grp]; + com_work_head[grp] = work; if (!work->next) - com_work_tail[thread] = work; + com_work_tail[grp] = work; found = true; break; //found it, nothing else to do. } } //we've not actually added any work, so no need to signal - Sys_UnlockConditional(com_workercondition[thread]); + Sys_UnlockConditional(com_workercondition[grp]); } if (!found) Con_DPrintf("Might be in for a long wait for %s\n", (char*)priorityctx); } - Sys_LockConditional(com_workercondition[0]); + Sys_LockConditional(com_workercondition[WG_MAIN]); do { if (com_workererror) break; - while(COM_DoWork(0, true)) + while(COM_DoWork(WG_MAIN, true)) { //give up as soon as we're done if (*address != value) @@ -5084,8 +5123,8 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value) //if our object's state has changed, we're done if (*address != value) break; - } while (Sys_ConditionWait(com_workercondition[0])); - Sys_UnlockConditional(com_workercondition[0]); + } while (Sys_ConditionWait(com_workercondition[WG_MAIN])); + Sys_UnlockConditional(com_workercondition[WG_MAIN]); // Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx); } @@ -5094,7 +5133,7 @@ static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b) { double *timestamp = data; if (!b) - COM_AddWork(0, COM_WorkerPing, ctx, data, 0, 1); + COM_AddWork(WG_MAIN, COM_WorkerPing, ctx, data, 0, 1); else { Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp); @@ -5102,35 +5141,66 @@ static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b) } static void COM_WorkerTest_f(void) { - if (com_workerthread) - { - double *timestamp = Z_Malloc(sizeof(*timestamp)); - *timestamp = Sys_DoubleTime(); - COM_AddWork(1, COM_WorkerPing, NULL, timestamp, 0, 0); - } - else - Con_Printf("Worker is not active.\n"); + double *timestamp = Z_Malloc(sizeof(*timestamp)); + *timestamp = Sys_DoubleTime(); + COM_AddWork(WG_LOADER, COM_WorkerPing, NULL, timestamp, 0, 0); } +static void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue) +{ + int i, count = var->ival; -cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling."); + if (!*var->string) + { + count = 4; + } + + //try to respond to any kill requests now, so we don't get surprised by the cvar changing too often. + while(COM_DoWork(WG_MAIN, false)) + ; + + for (i = 0; i < WORKERTHREADS; i++) + { + if (i >= count) + { + //higher thread indexes need to die. + com_worker[i].request = WR_DIE; //flag them all to die + } + else + { + //lower thread indexes need to be created + if (!com_worker[i].thread) + { + com_worker[i].request = WR_NONE; + com_worker[i].thread = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_worker[i], 0, 256*1024); + } + } + } + Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up to check their new death values. +} +cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling the main thread."); +cvar_t worker_count = CVARFDC("worker_count", "", CVAR_NOTFROMSERVER, "Specifies the number of worker threads to utilise.", COM_WorkerCount_Change); static void COM_InitWorkerThread(void) { int i; //in theory, we could run multiple workers, signalling a different one in turn for each bit of work. com_resourcemutex = Sys_CreateMutex(); - for (i = 0; i < WORKERTHREADS; i++) + for (i = 0; i < WG_COUNT; i++) { com_workercondition[i] = Sys_CreateConditional(); } - if (!COM_CheckParm("-noworker")) + com_liveworkers[WG_MAIN] = 1; + + //technically its ready now... + + if (COM_CheckParm("-noworker")) { - for (i = 1; i < WORKERTHREADS; i++) - { - com_workerthread[i] = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_workerthread[i], 0, 256*1024); - } + worker_count.string = "0"; + worker_count.flags |= CVAR_NOSET; } + Cvar_Register(&worker_count, NULL); + Cvar_ForceCallback(&worker_count); Cmd_AddCommand ("worker_test", COM_WorkerTest_f); Cvar_Register(&worker_flush, NULL); diff --git a/engine/common/common.h b/engine/common/common.h index e330bce90..b7db76f8c 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -429,10 +429,16 @@ typedef struct { } flocation_t; struct vfsfile_s; -typedef enum {FSLFRT_IFFOUND, FSLFRT_LENGTH, FSLFRT_DEPTH_OSONLY, FSLFRT_DEPTH_ANYPATH} FSLF_ReturnType_e; +#define FSLF_IFFOUND 0 //returns true (found) / false (not found) +#define FSLF_DEPTH_EXPLICIT 1 //retrieves relative depth (ie: lower = higher priority) for determining which gamedir a file was from +#define FSLF_DEPTH_INEXPLICIT 2 //depth is incremented for EVERY package, not just system/explicit paths. +#define FSLF_SECUREONLY (1u<<4) //ignore files from downloaded packages (ie: configs) +#define FSLF_DONTREFERENCE (1u<<5) //don't add any reference flags to packages +#define FSLF_IGNOREPURE (1u<<6) //use only the client's package list, ignore any lists obtained from the server (including any reordering) +#define FSLF_IGNORELINKS (1u<<7) //ignore any pak/pk3 symlinks. system ones may still be followed. + //if loc is valid, loc->search is always filled in, the others are filled on success. -//returns -1 if couldn't find. -int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation_t *loc); +int FS_FLocateFile(const char *filename, unsigned int flags, flocation_t *loc); struct vfsfile_s *FS_OpenReadLocation(flocation_t *location); char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced); @@ -442,8 +448,8 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); //returns false if the name is invalid. void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags); -#define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,ignorepacks?FSLFRT_DEPTH_OSONLY:FSLFRT_DEPTH_ANYPATH, NULL) -#define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLFRT_IFFOUND, NULL) +#define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,FSLF_DONTREFERENCE|(ignorepacks?FSLF_DEPTH_EXPLICIT:FSLF_DEPTH_INEXPLICIT), NULL) +#define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLF_IFFOUND, NULL) typedef struct vfsfile_s { diff --git a/engine/common/fs.c b/engine/common/fs.c index aa83f6b72..645fd3ce5 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -620,10 +620,11 @@ static unsigned int fs_pureseed; //used as a key so the server knows we're obeyi int QDECL COM_FileSize(const char *path) { - int len; flocation_t loc; - len = FS_FLocateFile(path, FSLFRT_LENGTH, &loc); - return len; + if (FS_FLocateFile(path, FSLF_IFFOUND, &loc)) + return loc.len; + else + return -1; } //appends a / on the end of the directory if it does not already have one. @@ -744,7 +745,7 @@ void COM_Locate_f (void) char *f = Cmd_Argv(1); if (strchr(f, '^')) //fte's filesystem is assumed to be utf-8, but that doesn't mean that console input is. and I'm too lazy to utf-8ify the string (in part because markup can be used to exploit ascii assumptions). Con_Printf("Warning: filename contains markup. If this is because of unicode, set com_parseutf8 1\n"); - if (FS_FLocateFile(f, FSLFRT_LENGTH, &loc)>=0) + if (FS_FLocateFile(f, FSLF_IFFOUND, &loc)) { if (!*loc.rawname) { @@ -1000,7 +1001,7 @@ Sets com_filesize and one of handle or file */ //if loc is valid, loc->search is always filled in, the others are filled on success. //returns -1 if couldn't find. -int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation_t *loc) +int FS_FLocateFile(const char *filename, unsigned int lflags, flocation_t *loc) { int depth=0; searchpath_t *search; @@ -1026,7 +1027,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation goto fail; } - if (com_fs_cache.ival && !com_fschanged) + if (com_fs_cache.ival && !com_fschanged && !(lflags & FSLF_IGNOREPURE)) { pf = Hash_GetInsensitive(&filesystemhash, filename); if (!pf) @@ -1035,17 +1036,19 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation else pf = NULL; - if (com_purepaths && found == FF_NOTFOUND) + if (com_purepaths && found == FF_NOTFOUND && !(lflags & FSLF_IGNOREPURE)) { //check if its in one of the 'pure' packages. these override the default ones. for (search = com_purepaths ; search ; search = search->nextpure) { - depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); + if ((lflags & FSLF_SECUREONLY) && !(search->flags & SPF_UNTRUSTED)) + continue; + depth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_EXPLICIT)); fs_finds++; found = search->handle->FindFile(search->handle, loc, filename, pf); if (found) { - if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH) + if (!(lflags & FSLF_DONTREFERENCE)) { if ((search->flags & fs_referencetype) != fs_referencetype) Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename); @@ -1057,17 +1060,19 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation } } - if (fs_puremode < 2 && found == FF_NOTFOUND) + if (((lflags & FSLF_IGNOREPURE) || fs_puremode < 2) && found == FF_NOTFOUND) { // optionally check the non-pure paths too. for (search = com_searchpaths ; search ; search = search->next) { - depth += ((search->flags & SPF_EXPLICIT) || returntype == FSLFRT_DEPTH_ANYPATH); + if ((lflags & FSLF_SECUREONLY) && !(search->flags & SPF_UNTRUSTED)) + continue; + depth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_EXPLICIT)); fs_finds++; found = search->handle->FindFile(search->handle, loc, filename, pf); if (found) { - if (returntype != FSLFRT_DEPTH_OSONLY && returntype != FSLFRT_DEPTH_ANYPATH) + if (!(lflags & FSLF_DONTREFERENCE)) { if ((search->flags & fs_referencetype) != fs_referencetype) Con_DPrintf("%s became referenced due to %s\n", search->purepath, filename); @@ -1079,7 +1084,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation } } fail: - if (found == FF_SYMLINK) + if (found == FF_SYMLINK && !(lflags & FSLF_IGNORELINKS)) { static int blocklink; if (blocklink < 4 && loc->len < MAX_QPATH) @@ -1125,7 +1130,7 @@ fail: //and locate that instead. blocklink++; - depth = FS_FLocateFile(mergedname, returntype, loc); + depth = FS_FLocateFile(mergedname, lflags, loc); blocklink--; if (!loc->search) Con_Printf("Symlink %s -> %s (%s) is dead\n", filename, targname, mergedname); @@ -1143,20 +1148,14 @@ fail: else Con_Printf("Failed\n"); */ - if (returntype == FSLFRT_IFFOUND) - return (found != FF_NOTFOUND) && (loc->len != -1); - else if (returntype == FSLFRT_LENGTH) - { - if (found == FF_NOTFOUND) - return -1; - return loc->len; - } - else + if (lflags & (FSLF_DEPTH_EXPLICIT | FSLF_DEPTH_INEXPLICIT)) { if (found == FF_NOTFOUND) return 0x7fffffff; return depth; } + else + return (found != FF_NOTFOUND) && (loc->len != -1); } char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced) @@ -1704,7 +1703,7 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r break; } - FS_FLocateFile(filename, FSLFRT_IFFOUND, &loc); + FS_FLocateFile(filename, FSLF_IFFOUND, &loc); if (loc.search) { @@ -1856,9 +1855,8 @@ qbyte *COM_LoadFile (const char *path, int usehunk, size_t *filesize) qbyte *buf; qofs_t len; flocation_t loc; - FS_FLocateFile(path, FSLFRT_LENGTH, &loc); - - if (!loc.search) + + if (!FS_FLocateFile(path, FSLF_IFFOUND, &loc) || !loc.search) return NULL; //wasn't found if (loc.len > 0x7fffffff) //don't malloc 5000gb sparse files or anything crazy on a 32bit system... @@ -2834,7 +2832,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) if (search) found = search->FindFile(search, &loc, name, NULL); else - found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); + found = FS_FLocateFile(name, FSLF_IFFOUND, &loc); if (found) { f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); @@ -2855,7 +2853,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) if (search) found = search->FindFile(search, &loc, name, NULL); else - found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); + found = FS_FLocateFile(name, FSLF_IFFOUND, &loc); if (found) { f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); @@ -2939,13 +2937,19 @@ char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum) int numpaks = 0; searchpath_t *sp; - FS_FLocateFile("vm/cgame.qvm", FSLFRT_LENGTH, &loc); - Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); - basechecksum ^= loc.search->crc_reply; + if (FS_FLocateFile("vm/cgame.qvm", FSLF_IFFOUND, &loc)) + { + Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); + basechecksum ^= loc.search->crc_reply; + } + else Q_strncatz(buffer, va("%i ", 0), maxlen); - FS_FLocateFile("vm/ui.qvm", FSLFRT_LENGTH, &loc); - Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); - basechecksum ^= loc.search->crc_reply; + if (FS_FLocateFile("vm/ui.qvm", FSLF_IFFOUND, &loc)) + { + Q_strncatz(buffer, va("%i ", loc.search->crc_reply), maxlen); + basechecksum ^= loc.search->crc_reply; + } + else Q_strncatz(buffer, va("%i ", 0), maxlen); Q_strncatz(buffer, "@ ", maxlen); @@ -4509,12 +4513,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean for (i = 0; i < countof(vidfile); i++) { - FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); //q1 + FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); //q1 vidpath[i] = loc.search?loc.search->handle:NULL; } for (i = 0; i < countof(conffile); i++) { - FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); //q1 + FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); //q1 confpath[i] = loc.search?loc.search->handle:NULL; } @@ -4689,7 +4693,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean { for (i = 0; i < countof(vidfile); i++) { - FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); + FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); if (vidpath[i] != (loc.search?loc.search->handle:NULL)) { vidrestart = true; @@ -4700,7 +4704,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean for (i = 0; i < countof(conffile); i++) { - FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); + FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); if (confpath[i] != (loc.search?loc.search->handle:NULL)) { reloadconfigs = true; diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 7ffd64980..06118c1b3 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -1365,7 +1365,12 @@ void Plug_Initialise(qboolean fromgamedir) if (!numplugbuiltins) { Cvar_Register(&plug_sbar, "plugins"); - Cvar_Register(&plug_loaddefault, "plugins"); +#ifdef SUBSERVERS + if (!SSV_IsSubServer()) +#endif + + Cvar_Register(&plug_loaddefault, "plugins"); + Cmd_AddCommand("plug_closeall", Plug_CloseAll_f); Cmd_AddCommand("plug_close", Plug_Close_f); Cmd_AddCommand("plug_load", Plug_Load_f); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 262019a87..4faaa0337 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2145,7 +2145,7 @@ void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo qboolean makereferenced = prinst->callargc>1?G_FLOAT(OFS_PARM1):true; flocation_t loc; - if (FS_FLocateFile(srcname, FSLFRT_IFFOUND, &loc)) + if (FS_FLocateFile(srcname, FSLF_IFFOUND, &loc)) { srcname = FS_WhichPackForLocation(&loc, makereferenced); if (srcname == NULL) diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 77765f980..eefd7025a 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -844,7 +844,7 @@ enum clcq2_ops_e enum { TE_SPIKE = 0, TE_SUPERSPIKE = 1, - TE_GUNSHOT = 2, + TE_GUNSHOT = 2, //qw has count byte, nq does not TE_EXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq. TE_TAREXPLOSION = 4, TE_LIGHTNING1 = 5, @@ -869,6 +869,7 @@ enum { TEQW_BEAM = 18, //use the builtin, luke. TEQW_EXPLOSION2 = 19, //use the builtin, luke. TEQW_EXPLOSIONNOSPRITE = 20, + TE_GUNSHOT_NQCOMPAT = 21, //nq has count byte, qw does not // hexen 2 TEH2_STREAM_LIGHTNING_SMALL = 24, diff --git a/engine/common/q3common.c b/engine/common/q3common.c index 9a199ae17..2d493f72e 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -23,7 +23,7 @@ int VM_fopen (char *name, int *handle, int fmode, int owner) size_t insize; if (!handle) - return FS_FLocateFile(name, FSLFRT_IFFOUND, NULL); + return FS_FLocateFile(name, FSLF_IFFOUND, NULL); *handle = 0; diff --git a/engine/common/sys_win_threads.c b/engine/common/sys_win_threads.c index 5cd82cef1..d230dd063 100644 --- a/engine/common/sys_win_threads.c +++ b/engine/common/sys_win_threads.c @@ -354,23 +354,20 @@ qboolean Sys_ConditionWait(void *condv) #endif EnterCriticalSection(&cv->countlock); done = cv->release > 0 && cv->waitgeneration != mygen; - LeaveCriticalSection(&cv->countlock); if (done) + { + cv->waiters--; + cv->release--; + done = cv->release == 0; + if (done) + ResetEvent(cv->evnt); + LeaveCriticalSection(&cv->countlock); break; + } + LeaveCriticalSection(&cv->countlock); } EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition - - // update waiting count and alert signaling thread that we're done to avoid the deadlock condition - EnterCriticalSection(&cv->countlock); - cv->waiters--; - cv->release--; - done = cv->release == 0; - LeaveCriticalSection(&cv->countlock); - - if (done) - ResetEvent(cv->evnt); - return true; } @@ -384,7 +381,8 @@ qboolean Sys_ConditionSignal(void *condv) EnterCriticalSection(&cv->countlock); if (cv->waiters > cv->release) { - SetEvent(cv->evnt); + if (!cv->release) + SetEvent(cv->evnt); cv->release++; cv->waitgeneration++; } @@ -405,7 +403,8 @@ qboolean Sys_ConditionBroadcast(void *condv) EnterCriticalSection(&cv->countlock); if (cv->waiters > 0) { - SetEvent(cv->evnt); + if (!cv->release) + SetEvent(cv->evnt); cv->release = cv->waiters; cv->waitgeneration++; } diff --git a/engine/common/world.h b/engine/common/world.h index ad1101aa9..fc83b4cd6 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -253,6 +253,7 @@ void QDECL World_ReleaseCollisionMesh(wedict_t *ed); void World_Destroy (world_t *w); void World_RBE_Start(world_t *world); +void World_RBE_Shutdown(world_t *world); void World_ClearWorld (world_t *w); diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index c3dcc036e..cdb9e5f73 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1264,7 +1264,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) lightdir[2] = 1; } - if (!r_vertexdlights.ival && r_dynamic.ival) + if (!r_vertexdlights.ival && r_dynamic.ival > 0) { float *org = e->origin; if (e->flags & RF_WEAPONMODEL) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 570bd924e..b19670975 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -4870,7 +4870,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) // r_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]); r_refdef.recurse+=1; //paranoid, should stop potential infinite loops - GLBE_SubmitMeshes(true, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE); r_refdef.recurse-=1; GLBE_FBO_Pop(oldfbo); @@ -4886,25 +4886,24 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) } } -void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop) +void GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop) { - model_t *model = cl.worldmodel; int i; int portaldepth = r_portalrecursion.ival; for (i = start; i <= stop; i++) { - if (drawworld) + if (worldbatches) { if (i == SHADER_SORT_PORTAL && r_refdef.recurse < portaldepth) { - GLBE_SubmitMeshesPortals(model->batches, shaderstate.mbatches[i]); + GLBE_SubmitMeshesPortals(worldbatches, shaderstate.mbatches[i]); if (!r_refdef.recurse && r_portalonly.ival) return; } - GLBE_SubmitMeshesSortList(model->batches[i]); + GLBE_SubmitMeshesSortList(worldbatches[i]); } GLBE_SubmitMeshesSortList(shaderstate.mbatches[i]); } @@ -4941,6 +4940,8 @@ static void BE_UpdateLightmaps(void) continue; if (lm->modified) { + int t = lm->rectchange.t; //pull them out now, in the hopes that it'll be more robust with respect to r_dynamic -1 + int b = lm->rectchange.b; lm->modified = false; if (!TEXVALID(lm->lightmap_texture)) { @@ -4957,14 +4958,14 @@ static void BE_UpdateLightmaps(void) else { GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture); - qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, lm->rectchange.t, - lm->width, lm->rectchange.h, glformat, gltype, - lm->lightmaps+(lm->rectchange.t) *lm->width*lightmap_bytes); + qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, + lm->width, b-t, glformat, gltype, + lm->lightmaps+t *lm->width*lightmap_bytes); } lm->rectchange.l = lm->width; lm->rectchange.t = lm->height; - lm->rectchange.h = 0; - lm->rectchange.w = 0; + lm->rectchange.r = 0; + lm->rectchange.b = 0; } } } @@ -4990,7 +4991,7 @@ void GLBE_BaseEntTextures(void) batch_t **ob = shaderstate.mbatches; shaderstate.mbatches = batches; BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode); - GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); + GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); GLBE_SelectEntity(&r_worldentity); shaderstate.mbatches = ob; } @@ -5340,7 +5341,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) } /*do portals*/ BE_SelectMode(BEM_STANDARD); - GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); BE_SelectMode(BEM_DEPTHNORM); if (!shaderstate.depthnormshader) @@ -5393,7 +5394,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) } /*draw surfaces that can be drawn this way*/ - GLBE_SubmitMeshes(true, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE); /*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/ GLBE_FBO_Sources(shaderstate.tex_normals, r_nulltex); @@ -5405,7 +5406,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) GLBE_SelectEntity(&r_worldentity); /*now draw the prelights*/ - GLBE_SubmitMeshes(true, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT); /*final reconfigure - now drawing final surface data onto true framebuffer*/ GLBE_FBO_Pop(oldfbo); @@ -5414,7 +5415,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) /*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/ GLBE_SelectEntity(&r_worldentity); - GLBE_SubmitMeshes(true, SHADER_SORT_SKY, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_SKY, SHADER_SORT_NEAREST); #ifdef RTLIGHTS /*regular lighting now*/ @@ -5426,7 +5427,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) qglClearColor (1,0,0,1); } -void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) +void GLBE_DrawWorld (batch_t **worldbatches, qbyte *vis) { extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps; batch_t *batches[SHADER_SORT_COUNT]; @@ -5479,7 +5480,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) GLBE_SelectEntity(&r_worldentity); BE_UpdateLightmaps(); - if (drawworld) + if (worldbatches) { if (gl_overbright.modified) { @@ -5493,7 +5494,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) } #ifdef RTLIGHTS - if (drawworld && r_shadow_realtime_world.ival) + if (worldbatches && r_shadow_realtime_world.ival) shaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value; else #endif @@ -5517,11 +5518,11 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) BE_SelectMode(BEM_STANDARD); RSpeedRemark(); - GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); + GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); RSpeedEnd(RSPEED_WORLD); #ifdef RTLIGHTS - if (drawworld) + if (worldbatches) { RSpeedRemark(); TRACE(("GLBE_DrawWorld: drawing lights\n")); @@ -5535,7 +5536,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) shaderstate.identitylighting = 1; - GLBE_SubmitMeshes(true, SHADER_SORT_DECAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(worldbatches, SHADER_SORT_DECAL, SHADER_SORT_NEAREST); /* if (r_refdef.gfog_alpha) { @@ -5550,7 +5551,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) { BE_SelectMode(BEM_WIREFRAME); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); BE_SelectMode(BEM_STANDARD); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } @@ -5559,14 +5560,14 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) } else { - GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); #ifdef GL_LINE //no gles if (r_wireframe.ival && qglPolygonMode) { BE_SelectMode(BEM_WIREFRAME); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); BE_SelectMode(BEM_STANDARD); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 49dc2e105..88df66505 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -854,7 +854,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil } error = FT_Err_Cannot_Open_Resource; - if (FS_FLocateFile(fontfilename, FSLFRT_LENGTH, &loc)>0) + if (FS_FLocateFile(fontfilename, FSLF_IFFOUND, &loc)) { if (*loc.rawname && !loc.offset) { diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 76030f236..043dbec20 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -512,8 +512,8 @@ static qboolean Terr_InitLightmap(hmsection_t *s, qboolean initialise) lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE; } return s->lightmap>=0; @@ -1452,8 +1452,8 @@ static void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s) lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE; } for (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++) { @@ -2101,7 +2101,7 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc) if (!Terr_SaveSection(hm, s, x, y, false)) return false; - return FS_FLocateFile(name, FSLFRT_IFFOUND, loc); + return FS_FLocateFile(name, FSLF_IFFOUND, loc); } #endif @@ -2240,7 +2240,7 @@ static qboolean Terr_Collect(heightmap_t *hm) #endif /*purge all sections, but not root -lightmaps only are purged whenever the client rudely kills lightmaps +lightmaps only are purged whenever the client rudely kills lightmaps (purges all lightmaps on map changes, to cope with models/maps potentially being unloaded) we'll reload those when its next seen. (lightmaps will already have been destroyed, so no poking them) */ @@ -2312,6 +2312,7 @@ void Terr_PurgeTerrainModel(model_t *mod, qboolean lightmapsonly, qboolean light // Con_Printf("PostPurge: %i lm chunks used, %i unused\n", hm->numusedlmsects, hm->numunusedlmsects); } + void Terr_FreeModel(model_t *mod) { heightmap_t *hm = mod->terrain; @@ -2354,6 +2355,7 @@ void Terr_FreeModel(model_t *mod) mod->terrain = NULL; } } + #ifndef SERVERONLY void Terr_DrawTerrainWater(heightmap_t *hm, float *mins, float *maxs, struct hmwater_s *w) { @@ -3831,7 +3833,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) if (!s || s->loadstate != TSLS_LOADED) { - if ((tr->contents & tr->hm->exteriorcontents) || s->loadstate != TSLS_FAILED) + if ((tr->hitcontentsmask & tr->hm->exteriorcontents) || s->loadstate != TSLS_FAILED) { //you're not allowed to walk into sections that have not loaded. //might as well check the entire section instead of just one tile @@ -4101,6 +4103,7 @@ qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec hmtrace.htilesize = hmtrace.hm->sectionsize / (SECTHEIGHTSIZE-1); hmtrace.frac = 1; hmtrace.contents = 0; + hmtrace.hitcontentsmask = against; hmtrace.plane[0] = 0; hmtrace.plane[1] = 0; @@ -4389,8 +4392,8 @@ static unsigned char *ted_getlightmap(hmsection_t *s, int idx) lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE; lm = lightmap[s->lightmap]->lightmaps; lm += ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap_bytes; return lm; @@ -4449,8 +4452,8 @@ static void ted_dorelight(heightmap_t *hm) lightmap[s->lightmap]->modified = true; lightmap[s->lightmap]->rectchange.l = 0; lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.r = HMLMSTRIDE; + lightmap[s->lightmap]->rectchange.b = HMLMSTRIDE; } static void ted_sethole(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) { @@ -5606,8 +5609,8 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) lm->modified = true; lm->rectchange.l = 0; lm->rectchange.t = 0; - lm->rectchange.w = lm->width; - lm->rectchange.h = lm->height; + lm->rectchange.r = lm->width; + lm->rectchange.b = lm->height; in = br->faces[j].lightdata; out = lm->lightmaps + (br->faces[j].lmbase[1] * lm->width + br->faces[j].lmbase[0]) * lightmap_bytes; @@ -6159,6 +6162,21 @@ void CL_Parse_BrushEdit(void) brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes); if (!Brush_Deserialise(hm, &brush)) Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n"); + if (brush.id) + { + int i; + if (cls.demoplayback) + Terr_Brush_DeleteId(hm, brush.id); + else + { + for (i = 0; i < hm->numbrushes; i++) + { + brushes_t *br = &hm->wbrushes[i]; + if (br->id == brush.id) + return; //we already have it. assume we just edited it. + } + } + } Terr_Brush_Insert(mod, hm, &brush); } else @@ -6608,7 +6626,7 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s } dist = DotProduct (best, in_normals[j]); dist = in_distances[j] - dist; - if (dist < 0) + if (dist <= 0) //don't find coplanar brushes. add an epsilon if you need this. break; } if (j == in_numplanes) @@ -6658,6 +6676,10 @@ void Terr_WriteMapFile(vfsfile_t *file, model_t *mod) int i; unsigned int entnum = 0; heightmap_t *hm; + + hm = mod->terrain; + if (hm && hm->exteriorcontents != FTECONTENTS_EMPTY) + VFS_WRITE(file, "terrain\n", 8); start = entities; while(entities) @@ -7278,13 +7300,36 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) return hm; } +#ifndef SERVERONLY void Mod_Terrain_Create_f(void) { + int x,y; + hmsection_t *s; + heightmap_t *hm; char *mname; - char *mdata; + char *mapdesc; + char *skyname; + char *groundname; + char *watername; + char *groundheight; + char *waterheight; + vfsfile_t *file; + model_t mod; + memset(&mod, 0, sizeof(mod)); + if (Cmd_Argc() < 2) + { + Con_Printf("%s: NAME \"DESCRIPTION\" SKYNAME DEFAULTGROUNDTEX DEFAULTHEIGHT DEFAULTWATER DEFAULTWATERHEIGHT\nGenerates a fresh maps/foo.hmp file. You may wish to edit it with notepad later to customise it. You will need csaddon.dat in order to edit the actual terrain.\n", Cmd_Argv(0)); + return; + } mname = va("maps/%s.hmp", Cmd_Argv(1)); - mdata = va( - "terrain\n" + + mapdesc = Cmd_Argv(2); if (!*mapdesc) mapdesc = Cmd_Argv(1); + skyname = Cmd_Argv(3); if (!*skyname) skyname = "sky1"; + groundname = Cmd_Argv(4); if (!*groundname) groundname = "default"; + groundheight = Cmd_Argv(5); if (!*groundheight) groundheight = "0"; + watername = Cmd_Argv(6); if (!*watername) watername = ""; + waterheight = Cmd_Argv(7); if (!*waterheight) waterheight = "1024"; + mod.entities = va( "{\n" "classname \"worldspawn\"\n" "message \"%s\"\n" @@ -7295,8 +7340,8 @@ void Mod_Terrain_Create_f(void) "_minysegment -2048\n" "_maxxsegment 2048\n" "_maxysegment 2048\n" - "//_defaultgroundtexture city4_2\n" - "//_defaultwatertexture *water2\n" + "//_defaultgroundtexture \"city4_2\"\n" + "//_defaultwatertexture \"*water2\"\n" "//_defaultgroundheight -1024\n" "//_defaultwaterheight 0\n" //hurrah, sea level. // "_tiles 64 64 8 8\n" @@ -7306,10 +7351,51 @@ void Mod_Terrain_Create_f(void) "origin \"0 0 1024\"\n" "}\n" , Cmd_Argv(2)); - COM_WriteFile(mname, FS_GAMEONLY, mdata, strlen(mdata)); - //FIXME: create 4 sections around the origin + mod.type = mod_heightmap; + mod.terrain = hm = Z_Malloc(sizeof(*hm)); + Terr_ParseEntityLump(mod.entities, hm); + hm->entitylock = Sys_CreateMutex(); + ClearLink(&hm->recycle); + Q_strncpyz(hm->path, Cmd_Argv(1), sizeof(hm->path)); + Q_strncpyz(hm->groundshadername, "terrainshader", sizeof(hm->groundshadername)); + hm->exteriorcontents = FTECONTENTS_SOLID; + + + for (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++) + for (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++) + Terr_GetSection(hm, x, y, TGS_TRYLOAD|TGS_DEFAULTONFAIL); + + for (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++) + for (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++) + { + s = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL); + if (s && (s->flags & (TSF_EDITED|TSF_DIRTY))) + { + Terr_InitLightmap(s, false); + Terr_SaveSection(hm, s, x, y, true); + } + } + + if (COM_FCheckExists(mname)) + { + Con_Printf("%s: already exists, not overwriting.\n", mname); + return; + } + FS_CreatePath(mname, FS_GAMEONLY); + file = FS_OpenVFS(mname, "wb", FS_GAMEONLY); + if (!file) + Con_Printf("unable to open %s\n", mname); + else + { + Terr_WriteMapFile(file, &mod); + VFS_CLOSE(file); + Con_Printf("Wrote %s\n", mname); + FS_FlushFSHashWritten(); + } + Terr_FreeModel(&mod); } +#endif //reads in the terrain a tile at a time, and writes it out again. //the new version will match our current format version. //this is mostly so I can strip out old format revisions... @@ -7426,9 +7512,9 @@ void Terr_Init(void) Cvar_Register(&mod_terrain_defaulttexture, "Terrain"); Cvar_Register(&mod_terrain_savever, "Terrain"); Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f); - Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f); Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f); #ifndef SERVERONLY + Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f); Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation."); #endif diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index 966d65220..207ba62ab 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -82,42 +82,16 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) hlmdl_tex_t *tex; hlmdl_bone_t *bones; hlmdl_bonecontroller_t *bonectls; - shader_t **shaders; + struct hlmodelshaders_s *shaders; void *texmem = NULL; /*~~*/ - //checksum the model - - if (mod->engineflags & MDLF_DOCRC) - { - unsigned short crc; - qbyte *p; - int len; - char st[40]; - - QCRC_Init(&crc); - for (len = com_filesize, p = buffer; len; len--, p++) - QCRC_ProcessByte(&crc, *p); - - sprintf(st, "%d", (int) crc); - Info_SetValueForKey (cls.userinfo[0], - (mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, - st, sizeof(cls.userinfo[0])); - - if (cls.state >= ca_connected) - { - CL_SendClientCommand(true, "setinfo %s %d", - (mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, - (int)crc); - } - } - //load the model into hunk model = ZG_Malloc(&mod->memgroup, sizeof(hlmodelcache_t)); - header = ZG_Malloc(&mod->memgroup, com_filesize); - memcpy(header, buffer, com_filesize); + header = ZG_Malloc(&mod->memgroup, fsize); + memcpy(header, buffer, fsize); #if defined(HLSERVER) && (defined(__powerpc__) || defined(__ppc__)) //this is to let bigfoot know when he comes to port it all... And I'm lazy. @@ -148,10 +122,12 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) texheader = NULL; if (!header->numtextures) { + size_t fz; char texmodelname[MAX_QPATH]; COM_StripExtension(mod->name, texmodelname, sizeof(texmodelname)); + Q_strncatz(texmodelname, "t.mdl", sizeof(texmodelname)); //no textures? eesh. They must be stored externally. - texheader = texmem = (hlmdl_header_t*)FS_LoadMallocFile(va("%st.mdl", texmodelname)); + texheader = texmem = (hlmdl_header_t*)FS_LoadMallocFile(texmodelname, &fz); if (texheader) { if (texheader->version != 10) @@ -184,18 +160,19 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); */ - model->header = (char *)header - (char *)model; - model->texheader = (char *)texheader - (char *)model; - model->textures = (char *)tex - (char *)model; - model->bones = (char *)bones - (char *)model; - model->bonectls = (char *)bonectls - (char *)model; + model->header = header; + model->bones = bones; + model->bonectls = bonectls; shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t)); - model->shaders = (char *)shaders - (char *)model; + model->shaders = shaders; for(i = 0; i < texheader->numtextures; i++) { - shaders[i] = R_RegisterSkin(va("%s_%i.tga", mod->name, i), mod->name); - shaders[i]->defaulttextures.base = R_LoadTexture8Pal24("", tex[i].w, tex[i].h, (qbyte *) texheader + tex[i].offset, (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset, IF_NOALPHA|IF_NOGAMMA); + Q_snprintfz(shaders[i].name, sizeof(shaders[i].name), "%s_%i.tga", mod->name, i); + memset(&shaders[i].defaulttex, 0, sizeof(shaders[i].defaulttex)); + shaders[i].defaulttex.base = Image_GetTexture(shaders[i].name, "", IF_NOALPHA, (qbyte *) texheader + tex[i].offset, (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset, tex[i].w, tex[i].h, TF_8PAL24); + shaders[i].w = tex[i].w; + shaders[i].h = tex[i].h; } model->numskins = texheader->numtextures; @@ -219,7 +196,7 @@ void *Mod_GetHalfLifeModelData(model_t *mod) return NULL; //halflife models only, please mc = Mod_Extradata(mod); - return (void*)((char*)mc + mc->header); + return (void*)mc->header; } #endif @@ -234,7 +211,7 @@ int HLMod_FrameForName(model_t *mod, const char *name) mc = Mod_Extradata(mod); - h = (hlmdl_header_t *)((char *)mc + mc->header); + h = mc->header; seqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex); for (i = 0; i < h->numseq; i++) @@ -256,7 +233,7 @@ int HLMod_BoneForName(model_t *mod, char *name) mc = Mod_Extradata(mod); - h = (hlmdl_header_t *)((char *)mc + mc->header); + h = mc->header; bones = (hlmdl_bone_t*)((char*)h+h->boneindex); for (i = 0; i < h->numbones; i++) @@ -278,7 +255,8 @@ int HLMod_BoneForName(model_t *mod, char *name) void HL_CalculateBones ( int offset, - int frame, + int frame1, + int frame2, float lerpfrac, vec4_t adjust, hlmdl_bone_t *bone, @@ -290,7 +268,6 @@ void HL_CalculateBones int i; vec3_t angle; float lerpifrac = 1-lerpfrac; - float t; /*~~~~~~~~~~*/ /* For each vector */ @@ -305,41 +282,30 @@ void HL_CalculateBones if(animation->offset[o] != 0) { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - int tempframe = frame; + int tempframe; hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]); + short f1, f2; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /* find values including the required frame */ + tempframe = frame1; while(animvalue->num.total <= tempframe) { tempframe -= animvalue->num.total; animvalue += animvalue->num.valid + 1; } - if(animvalue->num.valid > tempframe) + f1 = animvalue[min(animvalue->num.valid-1, tempframe)+1].value; + + /* frame2 is always higher than frame1, so keep searching for it, if its in a different block */ + tempframe += frame2-frame1; + while(animvalue->num.total <= tempframe) { - if(animvalue->num.valid > (tempframe + 1)) - { - //we can lerp that - t = animvalue[tempframe + 1].value * lerpifrac + lerpfrac * animvalue[tempframe + 2].value; - } - else - t = animvalue[animvalue->num.valid].value; - angle[i] = bone->value[o] + t * bone->scale[o]; - } - else - { - if(animvalue->num.total < tempframe + 1) - { - angle[i] += - (animvalue[animvalue->num.valid].value * lerpifrac + - lerpfrac * animvalue[animvalue->num.valid + 2].value) * - bone->scale[o]; - } - else - { - angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o]; - } + tempframe -= animvalue->num.total; + animvalue += animvalue->num.valid + 1; } + f2 = animvalue[min(animvalue->num.valid-1, tempframe)+1].value; + + angle[i] += (f1 * lerpifrac + lerpfrac * f2) * bone->scale[o]; } if(bone->bonecontroller[o] != -1) @@ -414,32 +380,67 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl static vec3_t positions[2]; static vec4_t quaternions[2], blended; - int frame; + int frame1, frame2; hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + ((unsigned int)seqnum>=model->header->numseq?0:seqnum); hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *) ((qbyte *) model->header + model->header->seqgroups) + sequence->seqindex; - hlmdl_anim_t *animation = (hlmdl_anim_t *) - ((qbyte *) model->header + sequencedata->data + sequence->index); + hlmdl_anim_t *animation; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + if (sequencedata->name[32]) + { + size_t fz; + if (sequence->seqindex >= MAX_ANIM_GROUPS) + { + Sys_Error("Too many animation sequence cache groups\n"); + return; + } + if (!model->animcache[sequence->seqindex]) + model->animcache[sequence->seqindex] = FS_LoadMallocGroupFile(model->memgroup, sequencedata->name+32, &fz); + if (!model->animcache[sequence->seqindex] || model->animcache[sequence->seqindex]->magic != *(int*)"IDSQ" || model->animcache[sequence->seqindex]->version != 10) + { + Sys_Error("Unable to load %s\n", sequencedata->name+32); + return; + } + animation = (hlmdl_anim_t *)((qbyte*)model->animcache[sequence->seqindex] + sequence->index); + } + else + animation = (hlmdl_anim_t *) ((qbyte *) model->header + sequencedata->data + sequence->index); + frametime *= sequence->timing; if (frametime < 0) frametime = 0; - frame = (int)frametime; - frametime -= frame; + frame1 = (int)frametime; + frametime -= frame1; + frame2 = frame1+1; if (!sequence->numframes) return; - if(frame >= sequence->numframes) + if(frame1 >= sequence->numframes) { if (sequence->loop) - frame %= sequence->numframes; + frame1 %= sequence->numframes; else - frame = sequence->numframes-1; + frame1 = sequence->numframes-1; + } + if(frame2 >= sequence->numframes) + { + if (sequence->loop) + frame2 %= sequence->numframes; + else + frame2 = sequence->numframes-1; + } + + if (frame2 < frame1) + { + i = frame2; + frame2 = frame1; + frame1 = i; + frametime = 1-frametime; } if (lastbone > model->header->numbones) @@ -489,10 +490,10 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl subblendfrac = 1; for(i = firstbone; i < lastbone; i++) { - HL_CalculateBones(0, frame, frametime, model->adjust, model->bones + i, animation + i, positions[0]); - HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); + HL_CalculateBones(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[0]); + HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); - HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i + model->header->numbones, quaternions[1]); + HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i + model->header->numbones, quaternions[1]); QuaternionSlerp(quaternions[0], quaternions[1], subblendfrac, blended); QuaternionGLMatrix(blended[0], blended[1], blended[2], blended[3], matrix); @@ -522,8 +523,8 @@ void HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, fl * convert it inside the routine - Inconsistant, but hey.. so's the whole model * format. */ - HL_CalculateBones(0, frame, frametime, model->adjust, model->bones + i, animation + i, positions[0]); - HL_CalculateBones(3, frame, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); + HL_CalculateBones(0, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, positions[0]); + HL_CalculateBones(3, frame1, frame2, frametime, model->adjust, model->bones + i, animation + i, quaternions[0]); QuaternionGLMatrix(quaternions[0][0], quaternions[0][1], quaternions[0][2], quaternions[0][3], matrix); matrix[0][3] = positions[0][0]; @@ -548,6 +549,7 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent, static vecV_t xyz[2048]; static vec3_t norm[2048]; static vec2_t st[2048]; + static byte_vec4_t vc[2048]; static index_t index[4096]; int count; int b; @@ -568,6 +570,7 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent, mesh->snormals_array = norm; //for rtlighting mesh->tnormals_array = norm; //for rtlighting mesh->indexes = index; + mesh->colors4b_array = vc; for (b = 0; b < MAX_BONE_CONTROLLERS; b++) model->controller[b] = curent->framestate.bonecontrols[b]; @@ -646,8 +649,14 @@ void R_HL_BuildFrame(hlmodel_t *model, hlmdl_model_t *amodel, entity_t *curent, st[vert][0] = order[2] * tex_s; st[vert][1] = order[3] * tex_t; + norm[vert][0] = 1; norm[vert][1] = 1; + norm[vert][2] = 1; + vc[vert][0] = 255; + vc[vert][1] = 255; + vc[vert][2] = 255; + vc[vert][3] = 255; order += 4; vert++; @@ -673,12 +682,12 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) static mesh_t bmesh, *mptr = &bmesh; //general model - model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header); -// model.texheader = (hlmdl_header_t *) ((char *)modelc + modelc->texheader); - model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures); - model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones); - model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls); - model.shaders = (shader_t **) ((char *)modelc + modelc->shaders); + model.header = modelc->header; + model.bones = modelc->bones; + model.bonectls = modelc->bonectls; + model.shaders = modelc->shaders; + model.animcache = modelc->animcache; + model.memgroup = &rent->model->memgroup; for (body = 0; body < model.header->numbodyparts; body++) { @@ -696,34 +705,40 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m; float tex_w; float tex_h; + struct hlmodelshaders_s *s; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ if (mesh->skinindex >= modelc->numskins) continue; + s = &model.shaders[modelc->skins[mesh->skinindex]]; + if (batches) { - shader_t *shader; - int sort; + int sort, j; b = BE_GetTempBatch(); if (!b) return; - shader = model.shaders[modelc->skins[mesh->skinindex]]; + if (!s->shader) + { + s->shader = R_RegisterSkin(s->name, rent->model->name); + R_BuildDefaultTexnums(&s->defaulttex, s->shader); + } b->buildmeshes = R_HL_BuildMesh; b->ent = rent; b->mesh = NULL; b->firstmesh = 0; b->meshes = 1; - b->skin = &shader->defaulttextures; + b->skin = NULL; b->texture = NULL; - b->shader = shader; + b->shader = s->shader; for (j = 0; j < MAXRLIGHTMAPS; j++) b->lightmap[j] = -1; b->surf_first = batchid; b->flags = 0; - sort = shader->sort; + sort = b->shader->sort; //fixme: we probably need to force some blend modes based on the surface flags. if (rent->flags & RF_FORCECOLOURMOD) b->flags |= BEF_FORCECOLOURMOD; @@ -755,8 +770,8 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) { if (batchid == b->surf_first) { - tex_w = 1.0f / model.textures[modelc->skins[mesh->skinindex]].w; - tex_h = 1.0f / model.textures[modelc->skins[mesh->skinindex]].h; + tex_w = 1.0f / s->w; + tex_h = 1.0f / s->h; b->mesh = &mptr; R_HL_BuildFrame(&model, amodel, b->ent, (short *) ((qbyte *) model.header + mesh->index), tex_w, tex_h, b->mesh[0]); diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index cfbb43d4d..4d8837d1a 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -46,8 +46,6 @@ texture_t r_notexture_mip_real; texture_t *r_notexture_mip = &r_notexture_mip_real; #endif -qboolean isnotmap = true; //used to not warp ammo models. - void CM_Init(void); void CM_Shutdown(void); @@ -1258,7 +1256,7 @@ static const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, un } -void Mod_FinishTexture(texture_t *tx, const char *loadname) +void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safetoloadfromwads) { #ifndef SERVERONLY extern cvar_t gl_shadeq1_name; @@ -1267,34 +1265,45 @@ void Mod_FinishTexture(texture_t *tx, const char *loadname) const char *origname = NULL; const char *shadername = tx->name; - - /*skies? just replace with the override sky*/ - if (!strncmp(tx->name, "sky", 3) && *cl.skyname) - tx->shader = R_RegisterCustom (va("skybox_%s", cl.skyname), SUF_NONE, Shader_DefaultSkybox, NULL); //just load the regular name. - else + if (!safetoloadfromwads) { - //remap to avoid bugging out on textures with the same name and different images (vanilla content sucks) - shadername = Mod_RemapBuggyTexture(shadername, tx->mips[0], tx->width*tx->height); - if (shadername) - origname = tx->name; - else - shadername = tx->name; - //find the * - if (!*gl_shadeq1_name.string || !strcmp(gl_shadeq1_name.string, "*")) - ; - else if (!(star = strchr(gl_shadeq1_name.string, '*')) || (strlen(gl_shadeq1_name.string)+strlen(tx->name)+1>=sizeof(altname))) //it's got to fit. - shadername = gl_shadeq1_name.string; + /*skies? just replace with the override sky*/ + if (!strncmp(tx->name, "sky", 3) && *cl.skyname) + tx->shader = R_RegisterCustom (va("skybox_%s", cl.skyname), SUF_NONE, Shader_DefaultSkybox, NULL); //just load the regular name. else { - strncpy(altname, gl_shadeq1_name.string, star-gl_shadeq1_name.string); //copy the left - altname[star-gl_shadeq1_name.string] = '\0'; - strcat(altname, shadername); //insert the * - strcat(altname, star+1); //add any final text. - shadername = altname; + //remap to avoid bugging out on textures with the same name and different images (vanilla content sucks) + shadername = Mod_RemapBuggyTexture(shadername, tx->mips[0], tx->width*tx->height); + if (shadername) + origname = tx->name; + else + shadername = tx->name; + + //find the * + if (!*gl_shadeq1_name.string || !strcmp(gl_shadeq1_name.string, "*")) + ; + else if (!(star = strchr(gl_shadeq1_name.string, '*')) || (strlen(gl_shadeq1_name.string)+strlen(tx->name)+1>=sizeof(altname))) //it's got to fit. + shadername = gl_shadeq1_name.string; + else + { + strncpy(altname, gl_shadeq1_name.string, star-gl_shadeq1_name.string); //copy the left + altname[star-gl_shadeq1_name.string] = '\0'; + strcat(altname, shadername); //insert the * + strcat(altname, star+1); //add any final text. + shadername = altname; + } + + tx->shader = R_RegisterCustom (shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); } - tx->shader = R_RegisterCustom (shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); + if (!tx->mips[0] && !safetoloadfromwads) + return; + } + else + { //already loaded. don't waste time / crash (this will be a dead pointer). + if (tx->mips[0]) + return; } if (!strncmp(tx->name, "sky", 3)) @@ -1559,10 +1568,10 @@ void Mod_NowLoadExternal(model_t *loadmodel) if (!tx) //e1m2, this happens continue; - if (tx->shader) + if (tx->mips[0]) continue; - Mod_FinishTexture(tx, loadname); + Mod_FinishTexture(tx, loadname, true); } #endif } @@ -1661,11 +1670,12 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean if (!litdata && r_loadlits.value) { char *litnames[] = { - "maps/%s.lit2", - "maps/%s.lit", + "%s.lit2", + "%s.lit", "lits/%s.lit2", "lits/%s.lit" }; + char litbasep[MAX_QPATH]; char litbase[MAX_QPATH]; int depth; int bestdepth = 0x7fffffff; @@ -1675,10 +1685,14 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean size_t litsize; qboolean inhibitvalidation = false; - COM_StripExtension(loadmodel->name, litbase, sizeof(litbase)); + COM_StripExtension(loadmodel->name, litbasep, sizeof(litbasep)); + COM_FileBase(loadmodel->name, litbase, sizeof(litbase)); for (i = 0; i < sizeof(litnames)/sizeof(litnames[0]); i++) { - Q_snprintfz(litname, sizeof(litname), litnames[i], litbase); + if (strchr(litnames[i], '/')) + Q_snprintfz(litname, sizeof(litname), litnames[i], litbase); + else + Q_snprintfz(litname, sizeof(litname), litnames[i], litbasep); depth = COM_FDepthFile(litname, false); if (depth < bestdepth) { @@ -1688,7 +1702,10 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean } if (best >= 0) { - Q_snprintfz(litname, sizeof(litname), litnames[best], litbase); + if (strchr(litnames[best], '/')) + Q_snprintfz(litname, sizeof(litname), litnames[best], litbase); + else + Q_snprintfz(litname, sizeof(litname), litnames[best], litbasep); litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize); } else @@ -2179,8 +2196,7 @@ qboolean Mod_LoadVertexNormals (model_t *loadmodel, qbyte *mod_base, lump_t *l) Mod_LoadSubmodels ================= */ -static qboolean hexen2map; -qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l) +qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map) { dq1model_t *inq; dh2model_t *inh; @@ -2193,7 +2209,7 @@ qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l) inh = (void *)(mod_base + l->fileofs); if (!inq->numfaces) { - hexen2map = true; + *hexen2map = true; if (l->filelen % sizeof(*inh)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); @@ -2231,7 +2247,7 @@ qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l) } else { - hexen2map = false; + *hexen2map = false; if (l->filelen % sizeof(*inq)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); @@ -3204,7 +3220,7 @@ static void Mod_Batches_AllocLightmaps(model_t *mod) } samps /= 4; samps = sqrt(samps); - if (j > 128 || !r_dynamic.ival) + if (j > 128 || r_dynamic.ival <= 0) samps *= 2; mod->lightmaps.width = bound(j, samps, LMBLOCK_SIZE_MAX); mod->lightmaps.height = bound(j, samps, LMBLOCK_SIZE_MAX); @@ -3500,7 +3516,7 @@ static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, i Mod_LoadLeafs ================= */ -static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm, qbyte *ptr, size_t len) +static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm, qboolean isnotmap, qbyte *ptr, size_t len) { mleaf_t *out; int i, j, count, p; @@ -3784,7 +3800,7 @@ void Mod_LoadCrouchHull(model_t *loadmodel) Mod_LoadClipnodes ================= */ -qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) +qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm, qboolean hexen2map) { dsclipnode_t *ins; dlclipnode_t *inl; @@ -4443,7 +4459,7 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b) if (!strncmp(loadname, "b_", 2)) Q_strncpyz(loadname, "bmodels", sizeof(loadname)); for(a = 0; a < mod->numtextures; a++) - Mod_FinishTexture(mod->textures[a], loadname); + Mod_FinishTexture(mod->textures[a], loadname, false); } } Mod_Batches_Build(mod, data); @@ -4483,7 +4499,7 @@ static void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaf //ignore the patch file if its in a different gamedir. //this file format sucks too much for other verification. - if (FS_FLocateFile(mod->name,FSLFRT_DEPTH_OSONLY, NULL) != FS_FLocateFile(patchname,FSLFRT_DEPTH_OSONLY, NULL)) + if (FS_FLocateFile(mod->name,FSLF_DEPTH_EXPLICIT, NULL) != FS_FLocateFile(patchname,FSLF_DEPTH_EXPLICIT, NULL)) return; patch->filelen = FS_LoadFile(patchname, &patch->fileptr); @@ -4547,6 +4563,8 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) int longm = false; char loadname[32]; qbyte *mod_base = buffer; + qboolean hexen2map = false; + qboolean isnotmap; #if (defined(ODE_STATIC) || defined(ODE_DYNAMIC)) qboolean ode = true; #else @@ -4662,7 +4680,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) noerrors = noerrors && Mod_LoadTextures (mod, mod_base, &header->lumps[LUMP_TEXTURES]); } TRACE(("Loading Submodels\n")); - noerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); + noerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS], &hexen2map); if (noerrors) { TRACE(("Loading CH\n")); @@ -4689,11 +4707,11 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) TRACE(("Loading Vis\n")); Mod_LoadVisibility (mod, mod_base, &header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen); } - noerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS], longm, vispatch.leafptr, vispatch.leaflen); + noerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS], longm, isnotmap, vispatch.leafptr, vispatch.leaflen); TRACE(("Loading Nodes\n")); noerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header->lumps[LUMP_NODES], longm); TRACE(("Loading Clipnodes\n")); - noerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header->lumps[LUMP_CLIPNODES], longm); + noerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header->lumps[LUMP_CLIPNODES], longm, hexen2map); if (noerrors) { TRACE(("Loading hull 0\n")); diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index a359583fe..b87727634 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -138,7 +138,11 @@ typedef struct batch_s /*caller-use, not interpreted by backend*/ union { - unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!) + struct + { + unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!) + unsigned int ebobatch; // + }; struct { unsigned int surf_first; @@ -952,6 +956,7 @@ typedef struct model_s vbo_t *vbos; void *terrain; batch_t *batches[SHADER_SORT_COUNT]; + unsigned int numbatches; struct { int first; //once built... diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 12d4c2b43..48261df41 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -192,6 +192,7 @@ index_t flashblend_indexes[FLASHBLEND_VERTS*3]; index_t flashblend_fsindexes[6] = {0, 1, 2, 0, 2, 3}; mesh_t flashblend_mesh; mesh_t flashblend_fsmesh; +shader_t *occluded_shader; shader_t *flashblend_shader; shader_t *lpplight_shader; @@ -264,6 +265,15 @@ void R_InitFlashblends(void) "}\n" "}\n" ); + occluded_shader = R_RegisterShader("flashblend_occlusiontest", SUF_NONE, + "{\n" + "program defaultadditivesprite\n" + "{\n" + "maskcolor\n" + "maskalpha\n" + "}\n" + "}\n" + ); lpplight_shader = NULL; } @@ -401,16 +411,97 @@ void R_RenderDlights (void) /*coronas use depth testing to compute visibility*/ if (coronastyle) { - extern cvar_t temp1; - if (r_coronas_occlusion.ival) + int method; + if (!*r_coronas_occlusion.string) + method = 4; + else + method = r_coronas_occlusion.ival; + if (method == 3 && qrenderer != QR_OPENGL) + method = 1; + if (method == 4 && (qrenderer != QR_OPENGL || !qglGenQueriesARB)) + method = 1; + + switch(method) { + case 2: if (TraceLineR(r_refdef.vieworg, l->origin, waste1, waste2)) continue; - } - else - { + break; + default: + case 1: if (TraceLineN(r_refdef.vieworg, l->origin, waste1, waste2)) continue; + break; + case 0: + break; +#ifdef GLQUAKE + case 3: + { + float depth; + vec3_t out; + float v[4], tempv[4]; + float mvp[16]; + + v[0] = l->origin[0]; + v[1] = l->origin[1]; + v[2] = l->origin[2]; + v[3] = 1; + + Matrix4_Multiply(r_refdef.m_projection, r_refdef.m_view, mvp); + Matrix4x4_CM_Transform4(mvp, v, tempv); + + tempv[0] /= tempv[3]; + tempv[1] /= tempv[3]; + tempv[2] /= tempv[3]; + + out[0] = (1+tempv[0])/2; + out[1] = (1+tempv[1])/2; + out[2] = (1+tempv[2])/2; + + out[0] = out[0]*r_refdef.pxrect.width + r_refdef.pxrect.x; + out[1] = out[1]*r_refdef.pxrect.height + r_refdef.pxrect.y; + if (tempv[3] < 0) + out[2] *= -1; + + if (out[2] < 0) + continue; + + //FIXME: in terms of performance, mixing reads+draws is BAD BAD BAD. SERIOUSLY BAD + qglReadPixels(out[0], out[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + if (depth < out[2]) + continue; + } + case 4: + { + GLuint res; + qboolean requery = true; + if (l->coronaocclusionquery) + { + qglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_AVAILABLE_ARB, &res); + if (res) + qglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_ARB, &l->coronaocclusionresult); + else if (!l->coronaocclusionresult) + continue; //query still running, nor currently visible. + else + requery = false; + } + else + { + qglGenQueriesARB(1, &l->coronaocclusionquery); + } + + if (requery) + { + qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, l->coronaocclusionquery); + R_BuildDlightMesh (l, intensity*10, 0.01, coronastyle); + BE_DrawMesh_Single(occluded_shader, &flashblend_mesh, NULL, beflags); + qglEndQueryARB(GL_SAMPLES_PASSED_ARB); + } + + if (!l->coronaocclusionresult) + continue; + } +#endif } } @@ -530,7 +621,7 @@ void R_PushDlights (void) return; #endif - if (!r_dynamic.ival || !cl.worldmodel) + if (r_dynamic.ival <= 0|| !cl.worldmodel) return; if (!cl.worldmodel->nodes) diff --git a/engine/gl/gl_rsurf.c b/engine/gl/gl_rsurf.c index b482405a4..97f5171b3 100644 --- a/engine/gl/gl_rsurf.c +++ b/engine/gl/gl_rsurf.c @@ -329,6 +329,7 @@ void GLBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch BZ_Free(vbo->vertdata); vbo->vertdata = NULL; } + vbo->vertcount = vcount; vbo->next = *vbochain; *vbochain = vbo; @@ -506,6 +507,7 @@ void GLBE_GenBrushModelVBO(model_t *mod) BZ_Free(vbo->vertdata); vbo->vertdata = NULL; } + vbo->vertcount = vcount; } #endif } @@ -524,8 +526,8 @@ void GLBE_UploadAllLightmaps(void) lm = lightmap[i]; lm->rectchange.l = lm->width; lm->rectchange.t = lm->height; - lm->rectchange.w = 0; - lm->rectchange.h = 0; + lm->rectchange.r = 0; + lm->rectchange.b = 0; if (!lm->modified) continue; lm->modified = false; diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 884e4fe1f..fed8ec89e 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -212,11 +212,11 @@ void GLSCR_UpdateScreen (void) Media_RecordFrame(); #endif + RSpeedShow(); + if (R2D_Flush) R2D_Flush(); - RSpeedEnd(RSPEED_TOTALREFRESH); - RSpeedShow(); RSpeedRemark(); VID_SwapBuffers(); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index f09c34719..025c0479f 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4597,6 +4597,9 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons imageflags = (basefmt==TF_SOLID8 || basefmt == TF_MIP4_SOLID8)?IF_NOALPHA:0; imageflags |= IF_MIPCAP; + if (basefmt == TF_MIP4_SOLID8 && palette && palette != host_basepal) + basefmt = TF_MIP4_8PAL24; + COM_StripExtension(imagename, imagename, sizeof(imagename)); aframes = max(1, shader->numdefaulttextures); @@ -4710,8 +4713,8 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (!TEXVALID(tex->fullbright)) { int s=-1; - if (mipdata[0]) - for(s = width*height; s-->0; ) + if (mipdata[0] && (!palette || palette == host_basepal)) + for(s = width*height-1; s>=0; s--) { if (mipdata[0][s] >= 256-vid.fullbright) break; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index e4c2aba5e..a22eaed1c 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -3144,8 +3144,6 @@ static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], Sh_DrawEntLighting(dl, colour); } -void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); - void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) { #ifdef GLQUAKE @@ -3221,7 +3219,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) BE_SelectMode(BEM_CREPUSCULAR); BE_SelectDLight(dl, colours, dl->axis, LSHADER_STANDARD); - GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); + GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); GLBE_FBO_Pop(oldfbo); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index bbe8a7ed6..96e6fbd05 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -62,6 +62,15 @@ void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid* BINDTEXFUNCPTR qglBindTexture; +void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids); +void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id); +void (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id); +void (APIENTRY *qglEndQueryARB)(GLenum target); +//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); +void (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); + /*glslang - arb_shader_objects gl core uses different names/distinctions from the extension */ @@ -1144,6 +1153,31 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) } #endif + if (!gl_config.gles && gl_config.glversion >= 1.5) + { + qglGenQueriesARB = (void *)getglext("glGenQueries"); + qglDeleteQueriesARB = (void *)getglext("glDeleteQueries"); + qglBeginQueryARB = (void *)getglext("glBeginQuery"); + qglEndQueryARB = (void *)getglext("glEndQuery"); + qglGetQueryObjectuivARB = (void *)getglext("glGetQueryObjectuiv"); + } + else if (GL_CheckExtension("GL_ARB_occlusion_query")) + { + qglGenQueriesARB = (void *)getglext("glGenQueriesARB"); + qglDeleteQueriesARB = (void *)getglext("glDeleteQueriesARB"); + qglBeginQueryARB = (void *)getglext("glBeginQueryARB"); + qglEndQueryARB = (void *)getglext("glEndQueryARB"); + qglGetQueryObjectuivARB = (void *)getglext("glGetQueryObjectuivARB"); + } + else + { + qglGenQueriesARB = NULL; + qglDeleteQueriesARB = NULL; + qglBeginQueryARB = NULL; + qglEndQueryARB = NULL; + qglGetQueryObjectuivARB = NULL; + } + if (!gl_config.gles && gl_config_nofixedfunc) qglDisableClientState(GL_VERTEX_ARRAY); } @@ -1822,7 +1856,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char * length[strings] = strlen(prstrings[strings]); strings++; } - if (ver >= 140) + if (ver >= 130) //gl3+ deprecated the varying keyword for geometry shaders to work properly { prstrings[strings] = "#define varying in\n" @@ -1862,7 +1896,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char * length[strings] = strlen(prstrings[strings]); strings++; } - if (ver >= 140) + if (ver >= 130) { prstrings[strings] = "#define attribute in\n" @@ -1871,7 +1905,7 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char * length[strings] = strlen(prstrings[strings]); strings++; } - if (gl_config.nofixedfunc || ver >= 140) + if (gl_config.nofixedfunc || ver >= 130) { prstrings[strings] = "attribute vec3 v_position1;\n" diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index b86073792..91321229a 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -759,7 +759,7 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int unsigned int stride = width; width /= 2; - if (width < 1 || height < 1 || stride != width*2) + if (width < 1 || height < 1 || stride != width*2 || !src) return; if (width*height > countof(trans)) diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 6a92998fe..64cee7341 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -693,6 +693,15 @@ extern GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target); extern void (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +extern void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids); +extern void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id); +extern void (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id); +extern void (APIENTRY *qglEndQueryARB)(GLenum target); +//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); + //glslang - arb_shader_objects extern FTEPFNGLCREATEPROGRAMOBJECTARBPROC qglCreateProgramObjectARB; extern FTEPFNGLDELETEOBJECTARBPROC qglDeleteProgramObject_; diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index fce1010ff..d493caa1f 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -764,4 +764,14 @@ typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void); #define GL_RGBA32F_ARB 0x8814 #endif + +#ifndef GL_SAMPLES_PASSED_ARB +#define GL_SAMPLES_PASSED_ARB 0x8914 +//#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +//#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#endif + + #endif diff --git a/engine/gl/ltface.c b/engine/gl/ltface.c index 7ec08c73c..76676e891 100644 --- a/engine/gl/ltface.c +++ b/engine/gl/ltface.c @@ -558,8 +558,6 @@ static void LightCalcPoints (llightinfo_t *l, float lmscale) VectorSubtract (facemid, surf, move); VectorNormalize (move); VectorMA (surf, 8, move, surf); - - P_RunParticleEffectType(surf, NULL, 1, pt_wizspike); } } } diff --git a/engine/gl/model_hl.h b/engine/gl/model_hl.h index 1932e83b8..9641de370 100644 --- a/engine/gl/model_hl.h +++ b/engine/gl/model_hl.h @@ -181,7 +181,7 @@ typedef struct int unknown7[2]; float unknown[4]; int unknown8; - int seqindex; + unsigned int seqindex; int unknown9[4]; } hlmdl_sequencelist_t; @@ -193,10 +193,17 @@ typedef struct typedef struct { char name[96]; /* should be split label[32] and name[64] */ - void * cache; + unsigned int cache; int data; } hlmdl_sequencedata_t; +typedef struct +{ + int magic; //IDSQ + int version; //10 + char name[64]; + int unk1; +} hlmdl_sequencefile_t; /* ----------------------------------------------------------------------------------------------------------------------- halflife model internal structure @@ -209,21 +216,27 @@ typedef struct /* Static pointers */ hlmdl_header_t *header; - hlmdl_header_t *texheader; - hlmdl_tex_t *textures; hlmdl_bone_t *bones; - hlmdl_bonecontroller_t *bonectls; - shader_t **shaders; + hlmdl_bonecontroller_t *bonectls; + struct hlmodelshaders_s *shaders; + hlmdl_sequencefile_t **animcache; + zonegroup_t *memgroup; } hlmodel_t; +#define MAX_ANIM_GROUPS 16 //submodel files containing anim data. typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing { - int header; - int texheader; - int textures; - int bones; - int bonectls; - int shaders; + hlmdl_header_t *header; + hlmdl_bone_t *bones; + hlmdl_bonecontroller_t *bonectls; + hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS]; + struct hlmodelshaders_s + { + char name[MAX_QPATH]; + texnums_t defaulttex; + shader_t *shader; + int w, h; + } *shaders; short *skins; int numskins; } hlmodelcache_t; diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 76a043046..145b1e035 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -717,12 +717,12 @@ batch_t *GLBE_GetTempBatch(void); void GLBE_GenBrushModelVBO(model_t *mod); void GLBE_ClearVBO(vbo_t *vbo); void GLBE_UploadAllLightmaps(void); -void GLBE_DrawWorld (qboolean drawworld, qbyte *vis); +void GLBE_DrawWorld (batch_t **worldbatches, qbyte *vis); qboolean GLBE_LightCullModel(vec3_t org, model_t *model); void GLBE_SelectEntity(entity_t *ent); qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); void GLBE_Scissor(srect_t *rect); -void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); +void GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop); //void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth); void GLBE_RenderToTextureUpdate2d(qboolean destchanged); void GLBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize); @@ -780,9 +780,9 @@ qboolean D3D11Shader_Init(unsigned int featurelevel); void D3D11BE_Reset(qboolean before); void D3D11BE_SetupViewCBuffer(void); void D3D11_UploadLightmap(lightmapinfo_t *lm); -void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); -void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); -void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); +void D3D11BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize); +void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray); +void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray); void D3D11BE_VBO_Destroy(vboarray_t *vearray); void D3D11BE_Scissor(srect_t *rect); diff --git a/engine/http/httpserver.c b/engine/http/httpserver.c index c6b7a3077..469775321 100644 --- a/engine/http/httpserver.c +++ b/engine/http/httpserver.c @@ -307,7 +307,8 @@ void HTTP_RunExisting (void) ammount = recv(cl->datasock, cl->inbuffer+cl->inbufferused, ammount, 0); if (ammount < 0) { - if (neterrno() != NET_EWOULDBLOCK) //they closed on us. Assume end. + int e = neterrno(); + if (e != NET_EWOULDBLOCK) //they closed on us. Assume end. { cl->closereason = "recv error"; } diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index ab55b0411..9ec7c08e4 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1651,6 +1651,14 @@ char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buf, int *bufo #undef AddS } +//just a simple helper that makes sure the s_name+s_file values are actually valid. some qccs generate really dodgy values intended to crash decompilers, but also crash debuggers too. +static char *PR_StaticString(progfuncs_t *progfuncs, string_t thestring) +{ + if (thestring <= 0 || thestring >= progfuncs->funcs.stringtablesize) + return "???"; + return thestring + progfuncs->funcs.stringtable; +} + char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufmax) { #define AddS(str) PR_Cat(buf, str, bufofs, bufmax) @@ -1691,9 +1699,9 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufm AddS (buffer); } if (!f->s_file) - sprintf(buffer, "\t\"%i:%s\"\n", progs, f->s_name+progfuncs->funcs.stringtable); + sprintf(buffer, "\t\"%i:%s\"\n", progs, PR_StaticString(progfuncs, f->s_name)); else - sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, f->s_name+progfuncs->funcs.stringtable, f->s_file+progfuncs->funcs.stringtable); + sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, PR_StaticString(progfuncs, f->s_name), PR_StaticString(progfuncs, f->s_file)); AddS (buffer); AddS ("\t{\n"); @@ -1707,10 +1715,10 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufm { if (local->type == ev_entity) { - sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->funcs.stringtable, ((eval_t*)(globalbase - f->locals+arg))->edict); + sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", PR_StaticString(progfuncs, local->s_name), ((eval_t*)(globalbase - f->locals+arg))->edict); } else - sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg), false)); + sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", PR_StaticString(progfuncs, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg), false)); if (local->type == ev_vector) arg+=2; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 6c18797f8..06e2d656a 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1072,17 +1072,17 @@ void PR_Compile_f(void) argv[4] = Cmd_Argv(1); argc = 5; } - if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) + if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL)) { //try the qc path argv[2] = "qc"; } - if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) + if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL)) { //try the progs path (yeah... gah) argv[2] = "progs"; } - if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLFRT_IFFOUND, NULL)) + if (!FS_FLocateFile(va("%s/%s", argv[2], argv[4]), FSLF_IFFOUND, NULL)) { //try the gamedir path argv[1] = argv[3]; @@ -3782,7 +3782,7 @@ static void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvar G_INT(OFS_RETURN) = G_INT(OFS_PARM0); /*touch the file, so any packs will be referenced. this is fte-specific behaviour.*/ - FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); + FS_FLocateFile(s, FSLF_IFFOUND, NULL); } int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) @@ -3802,7 +3802,7 @@ int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) Q_strncpyz(sv.strings.sound_precache[i], s, sizeof(sv.strings.sound_precache[i])); /*touch the file, so any packs will be referenced*/ - FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); + FS_FLocateFile(s, FSLF_IFFOUND, NULL); if (sv.state != ss_loading) { @@ -3868,7 +3868,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean else { /*touch the file, so any packs will be referenced*/ - FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); + FS_FLocateFile(s, FSLF_IFFOUND, NULL); } if (sv.state != ss_loading) @@ -11346,6 +11346,7 @@ void PR_DumpPlatform_f(void) VFS_PRINTF(f, "#pragma warning error Q101 /*too many parms*/\n"); VFS_PRINTF(f, "#pragma warning error Q105 /*too few parms*/\n"); + VFS_PRINTF(f, "#pragma warning error Q106 /*assignment to constant/lvalue*/\n"); VFS_PRINTF(f, "#pragma warning error Q208 /*system crc unknown*/\n"); VFS_PRINTF(f, "#pragma warning enable F301 /*non-utf-8 strings*/\n"); VFS_PRINTF(f, "#pragma warning enable F302 /*uninitialised locals*/\n"); diff --git a/engine/server/savegame.c b/engine/server/savegame.c index 11a62d905..a0c029496 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -608,7 +608,7 @@ qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolea return false; } - if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc)) + if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc)) { Con_Printf("Couldn't find %s.\n", name); return false; @@ -1493,7 +1493,7 @@ void SV_Loadgame_f (void) { flocation_t loc; char *name = va("saves/%s/game.gsv", savename); - if (!FS_FLocateFile(name, FSLFRT_IFFOUND, &loc)) + if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc)) Con_Printf("Couldn't find %s.\n", name); else if (!*loc.rawname || loc.offset) Con_Printf("%s is inside a package and cannot be used by the quake2 gamecode.\n", name); diff --git a/engine/server/sv_cluster.c b/engine/server/sv_cluster.c index f38a4a7af..4b0acfe6d 100644 --- a/engine/server/sv_cluster.c +++ b/engine/server/sv_cluster.c @@ -221,7 +221,10 @@ void MSV_MapCluster_f(void) //child processes return 0 and fall through memset(&sv, 0, sizeof(sv)); - if (atoi(Cmd_Argv(1))) + Q_strncpyz(sv.modelname, Cmd_Argv(1), sizeof(sv.modelname)); + if (!*sv.modelname) + Q_strncpyz(sv.modelname, "start", sizeof(sv.modelname)); + if (atoi(Cmd_Argv(2))) { Con_Printf("Opening database \"%s\"\n", sqlparams[3]); sv.logindatabase = SQL_NewServer("sqlite", sqlparams); @@ -626,7 +629,7 @@ void SSV_ReadFromControlServer(void) char *addr = MSG_ReadString(); int i; - Con_Printf("%s: got tookplayer\n", sv.name); + Con_Printf("%s: got tookplayer\n", sv.modelname); for (i = 0; i < svs.allocated_client_slots; i++) { @@ -640,19 +643,19 @@ void SSV_ReadFromControlServer(void) { if (!*addr) { - Con_Printf("%s: tookplayer: failed\n", sv.name); + Con_Printf("%s: tookplayer: failed\n", sv.modelname); Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo)); } else { - Con_Printf("%s: tookplayer: do transfer\n", sv.name); + Con_Printf("%s: tookplayer: do transfer\n", sv.modelname); // SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr)); SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr)); cl->redirect = 2; } } else - Con_Printf("%s: tookplayer: invalid player.\n", sv.name); + Con_Printf("%s: tookplayer: invalid player.\n", sv.modelname); } break; @@ -723,7 +726,7 @@ void SSV_ReadFromControlServer(void) } else { - Con_Printf("%s: server full!\n", sv.name); + Con_Printf("%s: server full!\n", sv.modelname); } j = MSG_ReadByte(); @@ -838,7 +841,7 @@ void SSV_UpdateAddresses(void) send.cursize = 2; MSG_WriteByte(&send, ccmd_serveraddress); - MSG_WriteString(&send, sv.name); + MSG_WriteString(&send, sv.modelname); for (i = 0; i < count; i++) MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i])); MSG_WriteByte(&send, 0); @@ -971,7 +974,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv pubsubserver_t *s = NULL; if (!s) - s = MSV_FindSubServerName(":start"); + s = MSV_FindSubServerName(va(":%s", sv.modelname)); if (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s)) SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index b544a63df..7e92b9eb3 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -739,7 +739,7 @@ void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, if (bits & U_COLORMAP) MSG_WriteByte (msg, to->colormap); if (bits & U_SKIN) - MSG_WriteByte (msg, to->skinnum); + MSG_WriteByte (msg, to->skinnum&0xff); if (bits & U_EFFECTS) MSG_WriteByte (msg, to->effects&0x00ff); if (bits & U_ORIGIN1) @@ -3647,7 +3647,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore cameras = NULL; #ifdef HLSERVER else if (svs.gametype == GT_HALFLIFE) - pvs = SVHL_Snapshot_SetupPVS(client, pvsbuffer, sizeof(pvsbuffer)); + SVHL_Snapshot_SetupPVS(client, cameras->pvs, sizeof(cameras->pvs)); #endif else SV_Snapshot_SetupPVS(client, cameras); @@ -3682,7 +3682,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore { #ifdef HLSERVER if (svs.gametype == GT_HALFLIFE) - SVHL_Snapshot_Build(client, pack, pvs, clent, ignorepvs); + SVHL_Snapshot_Build(client, pack, cameras->pvs, clent, ignorepvs); else #endif SV_Snapshot_BuildQ1(client, pack, cameras, clent); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 57f8e59f4..3d50155d4 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -223,7 +223,7 @@ baseline will be transmitted void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client); -void SVNQ_CreateBaseline (void) +void SVQ1_CreateBaseline (void) { edict_t *svent; int entnum; @@ -1587,7 +1587,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us // create a baseline for more efficient communications // SV_CreateBaseline (); if (svprogfuncs) - SVNQ_CreateBaseline(); + SVQ1_CreateBaseline(); #ifdef Q2SERVER SVQ2_BuildBaselines(); #endif diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 6b2cf5f0d..5b9707e52 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1748,7 +1748,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) //some gamecode can't cope with some extensions for some reasons... and I'm too lazy to fix the code to cope. if (svs.gametype == GT_HALFLIFE) - client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS; + client->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS; //baseline issues // client->maxmodels = 256; @@ -2593,6 +2593,7 @@ client_t *SVC_DirectConnect(void) Con_TPrintf ("%s:gamecode reject\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); return NULL; } + temp.hledict = newcl->hledict; } break; @@ -5030,7 +5031,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) cl->rate = ISNQCLIENT(cl)?10000:2500; //an nq client cannot cope with quakeworld's default rate, and typically doesn't have rate set either. val = Info_ValueForKey (cl->userinfo, "dupe"); - cl->netchan.dupe = atoi(val); + cl->netchan.dupe = bound(0, atoi(val), 5); val = Info_ValueForKey (cl->userinfo, "drate"); if (strlen(val)) diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 0ea7b597c..cdd297614 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2509,6 +2509,7 @@ qboolean SV_Physics (void) if (svs.gametype == GT_HALFLIFE) { SVHL_RunFrame(); + sv.world.physicstime += host_frametime; continue; } #endif diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 06be4f0bc..c6b5e62c4 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1367,6 +1367,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) client->nextservertimeupdate = sv.physicstime; */ +#ifdef HLSERVER + if (svs.gametype == GT_HALFLIFE) + return; +#endif + #ifdef NQPROT ent = client->edict; if (progstype != PROG_QW) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 33727c1e0..db870a80a 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -47,8 +47,8 @@ cvar_t sv_spectalk = SCVAR("sv_spectalk", "1"); cvar_t sv_mapcheck = SCVAR("sv_mapcheck", "1"); cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full"); -cvar_t sv_antilag = CVARFD("sv_antilag", "1", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline."); -cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "1", CVAR_SERVERINFO); +cvar_t sv_antilag = CVARFD("sv_antilag", "", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline."); +cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "", CVAR_SERVERINFO); #ifndef NEWSPEEDCHEATPROT cvar_t sv_cheatpc = CVARD("sv_cheatpc", "125", "If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated."); cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The interval between each speed-cheat check."); @@ -57,6 +57,9 @@ cvar_t sv_playermodelchecks = CVAR("sv_playermodelchecks", "0"); cvar_t sv_ping_ignorepl = CVARD("sv_ping_ignorepl", "0", "If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis."); cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "0", "Specifies the default protocol to use for new NQ clients. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required."); +cvar_t sv_minpitch = CVARAFD("minpitch", "", "sv_minpitch", CVAR_SERVERINFO, "Assumed to be -70"); +cvar_t sv_maxpitch = CVARAFD("maxpitch", "", "sv_maxpitch", CVAR_SERVERINFO, "Assumed to be 80"); + cvar_t sv_cmdlikercon = SCVAR("sv_cmdlikercon", "0"); //set to 1 to allow a password of username:password instead of the correct rcon password. cvar_t cmd_allowaccess = SCVAR("cmd_allowaccess", "0"); //set to 1 to allow cmd to execute console commands on the server. cvar_t cmd_gamecodelevel = SCVAR("cmd_gamecodelevel", STRINGIFY(RESTRICT_LOCAL)); //execution level which gamecode is told about (for unrecognised commands) @@ -2853,7 +2856,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam } #endif else - found = FS_FLocateFile(name, FSLFRT_IFFOUND, loc); + found = FS_FLocateFile(name, FSLF_IFFOUND, loc); //nexuiz names certain files as .wav but they're really .ogg on disk. if (!found && replacementname) @@ -2865,7 +2868,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam COM_StripExtension(name, tryogg, sizeof(tryogg)); COM_DefaultExtension(tryogg, ".ogg", sizeof(tryogg)); - found = FS_FLocateFile(tryogg, FSLFRT_IFFOUND, loc); + found = FS_FLocateFile(tryogg, FSLF_IFFOUND, loc); if (found) { name = *replacementname = va("%s", tryogg); @@ -2909,7 +2912,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam if (pakname && SV_AllowDownload(pakname)) { //return loc of the pak instead. - if (FS_FLocateFile(name, FSLFRT_IFFOUND, loc)) + if (FS_FLocateFile(name, FSLF_IFFOUND, loc)) { //its inside a pak file, return the name of this file instead *replacementname = pakname; @@ -4367,6 +4370,14 @@ void Cmd_Spiderpig_f(void) } void Cmd_Noclip_f (void) { +#ifdef HLSERVER + if (svs.gametype == GT_HALFLIFE) + { + HLSV_ClientCommand(host_client); + return; + } +#endif + if (!SV_MayCheat()) { SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n"); @@ -6991,7 +7002,7 @@ void SV_ExecuteClientMessage (client_t *cl) cl->delay = 0.2; } - if (sv_antilag.ival) + if (sv_antilag.ival || !*sv_antilag.string) { /* extern cvar_t temp1; @@ -7009,7 +7020,7 @@ void SV_ExecuteClientMessage (client_t *cl) } cl->laggedents_count = sv.allocated_client_slots; - cl->laggedents_frac = sv_antilag_frac.value; + cl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value; } else cl->laggedents_count = 0; @@ -7720,6 +7731,9 @@ void SV_UserInit (void) Cvar_Register (&sv_spectalk, cvargroup_servercontrol); Cvar_Register (&sv_mapcheck, cvargroup_servercontrol); + Cvar_Register (&sv_minpitch, cvargroup_servercontrol); + Cvar_Register (&sv_maxpitch, cvargroup_servercontrol); + Cvar_Register (&sv_fullredirect, cvargroup_servercontrol); Cvar_Register (&sv_antilag, cvargroup_servercontrol); Cvar_Register (&sv_antilag_frac, cvargroup_servercontrol); diff --git a/engine/server/svhl_game.c b/engine/server/svhl_game.c index 7825394c1..dcf008a0a 100644 --- a/engine/server/svhl_game.c +++ b/engine/server/svhl_game.c @@ -47,7 +47,7 @@ int lastusermessage; -string_t QDECL GHL_AllocString(char *string) +string_t QDECL GHL_AllocString(const char *string) { char *news; bi_begin(); @@ -58,7 +58,7 @@ string_t QDECL GHL_AllocString(char *string) bi_end(); return news - SVHL_Globals.stringbase; } -int QDECL GHL_PrecacheModel(char *name) +int QDECL GHL_PrecacheModel(const char *name) { int i; bi_trace(); @@ -69,7 +69,7 @@ int QDECL GHL_PrecacheModel(char *name) return 0; } - for (i=1 ; iv.flags; + const vec3_t up = {0, 0, 1}; + vec3_t move; + qboolean relink = true; + qboolean domove = true; + bi_trace(); - ignore("GHL_MoveToOrigin"); + + if (moveflags) + { //strafe. just move directly. + VectorSubtract(dest, ent->v.origin, move); + move[2] = 0; + VectorNormalize(move); + VectorMA(ent->v.origin, dist, move, move); + } + else + { + float yaw = DEG2RAD(ent->v.angles[1]); + move[0] = cos(yaw) * dist; + move[1] = sin(yaw) * dist; + move[2] = 0; + } + +// try the move + VectorCopy (ent->v.origin, oldorg); + VectorAdd (ent->v.origin, move, neworg); + +#if 0 +// flying monsters don't step up + if (eflags & (FL_SWIM | FL_FLY)) + { + // try one move with vertical motion, then one without + for (i=0 ; i<2 ; i++) + { + VectorAdd (ent->v.origin, move, neworg); + if (!noenemy) + { + enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy); + if (i == 0 && enemy->entnum) + { + VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end); + dz = DotProduct(end, axis[2]); + if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/ + dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2]; + if (dz > 40) + VectorMA(neworg, -8, up, neworg); + if (dz < 30) + VectorMA(neworg, 8, up, neworg); + } + } + trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent); + if (set_move_trace) + set_move_trace(world->progs, set_trace_globs, &trace); + + if (trace.fraction == 1) + { + if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID)) + continue; // swim monster left water + + if (domove) + VectorCopy (trace.endpos, ent->v->origin); + if (relink) + World_LinkEdict (world, ent, true); + return true; + } + + if (noenemy || !enemy->entnum) + break; + } + + return false; + } +#endif + +// push down from a step height above the wished position + VectorMA(neworg, movevars.stepheight, up, neworg); + VectorMA(neworg, movevars.stepheight*-2, up, end); + + trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent); + + if (trace.allsolid) + return false; + + if (trace.startsolid) + { + //move up by an extra step, if needed + VectorMA(neworg, -movevars.stepheight, up, neworg); + trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent); + if (trace.allsolid || trace.startsolid) + return false; + } + if (trace.fraction == 1) + { + // if monster had the ground pulled out, go ahead and fall + if ( (int)eflags & FL_PARTIALGROUND ) + { + if (domove) + { + VectorAdd (ent->v.origin, move, ent->v.origin); + if (relink) + SVHL_LinkEdict (ent, true); + ent->v.flags = (int)eflags & ~FL_ONGROUND; + } +// Con_Printf ("fall down\n"); + return true; + } + + return false; // walked off an edge + } + +// check point traces down for dangling corners + if (domove) + VectorCopy (trace.endpos, ent->v.origin); + +/* if (!World_CheckBottom (world, ent, up)) + { + if ( (int)ent->v->flags & FL_PARTIALGROUND ) + { // entity had floor mostly pulled out from underneath it + // and is trying to correct + if (relink) + SVHL_LinkEdict (ent, true); + return true; + } + + if (domove) + VectorCopy (oldorg, ent->v->origin); + return false; + } +*/ + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { +// Con_Printf ("back on ground\n"); + ent->v.flags &= ~FL_PARTIALGROUND; + } + ent->v.groundentity = trace.ent; + +// the move is ok + if (relink) + SVHL_LinkEdict (ent, true); + return true; } unk QDECL GHL_ChangeYaw(unk){notimpf(__func__);} unk QDECL GHL_ChangePitch(unk){notimpf(__func__);} @@ -243,7 +389,23 @@ hledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *valu } return SVHL_Edict; } -unk QDECL GHL_GetEntityIllum(unk){notimpf(__func__);} +void Sh_CalcPointLight(vec3_t point, vec3_t light); +int QDECL GHL_GetEntityIllum(hledict_t *ent) +{ + vec3_t diffuse, ambient, dir; + float lev = 0; +#if defined(RTLIGHTS) && !defined(SERVERONLY) + Sh_CalcPointLight(ent->v.origin, ambient); + lev += VectorLength(ambient); + + if (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value) +#endif + { + sv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, ent->v.origin, ambient, diffuse, dir); + lev += (VectorLength(ambient) + VectorLength(diffuse)/2.0)/256; + } + return lev * 255; //I assume its 0-255, no idea +} hledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius) { int i, j; @@ -281,7 +443,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed) int best = 0, i; float bestdist = 99999999; //HL maps are limited in size anyway float d; - int leafnum; + int clusternum; vec3_t ofs; hledict_t *other; @@ -290,7 +452,7 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed) //fixme: we need to track some state //a different client should be returned each call _per ent_ (so it can be used once per frame) - viewerpvs = sv.world.worldmodel->funcs.LeafPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0); + viewerpvs = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0); for (i = 0; i < svs.allocated_client_slots; i++) { @@ -302,8 +464,8 @@ hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed) if (svs.clients[i].spectator) continue; //ignore spectators - leafnum = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/ - if (viewerpvs[leafnum>>3] & (1<<(leafnum&7))) + clusternum = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/ + if (viewerpvs[clusternum>>3] & (1<<(clusternum&7))) { VectorSubtract(ed->v.origin, other->v.origin, ofs); d = DotProduct(ofs, ofs); @@ -455,11 +617,145 @@ int QDECL GHL_DropToFloor(hledict_t *ed) VectorCopy(tr.endpos, ed->v.origin); return tr.fraction != 0 && tr.fraction != 1; } -int QDECL GHL_WalkMove(hledict_t *ed, float yaw, float dist, int mode) +int QDECL GHL_WalkMove(hledict_t *ent, float yaw, float dist, int mode) { + //mode 0: no idea + //mode 1: no idea + //mode 2: test only +// float dz; + vec3_t oldorg, neworg, end; + trace_t trace; +// int i; + int eflags = ent->v.flags; + const vec3_t up = {0, 0, 1}; + vec3_t move; + qboolean relink = mode != 2; + qboolean domove = mode != 2; + bi_trace(); - ignore("walkmove"); - return 1; + + yaw = DEG2RAD(yaw); + move[0] = cos(yaw) * dist; + move[1] = sin(yaw) * dist; + move[2] = 0; + +// try the move + VectorCopy (ent->v.origin, oldorg); + VectorAdd (ent->v.origin, move, neworg); + +#if 0 +// flying monsters don't step up + if (eflags & (FL_SWIM | FL_FLY)) + { + // try one move with vertical motion, then one without + for (i=0 ; i<2 ; i++) + { + VectorAdd (ent->v.origin, move, neworg); + if (!noenemy) + { + enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy); + if (i == 0 && enemy->entnum) + { + VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end); + dz = DotProduct(end, axis[2]); + if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/ + dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2]; + if (dz > 40) + VectorMA(neworg, -8, up, neworg); + if (dz < 30) + VectorMA(neworg, 8, up, neworg); + } + } + trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent); + if (set_move_trace) + set_move_trace(world->progs, set_trace_globs, &trace); + + if (trace.fraction == 1) + { + if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID)) + continue; // swim monster left water + + if (domove) + VectorCopy (trace.endpos, ent->v->origin); + if (relink) + World_LinkEdict (world, ent, true); + return true; + } + + if (noenemy || !enemy->entnum) + break; + } + + return false; + } +#endif + +// push down from a step height above the wished position + VectorMA(neworg, movevars.stepheight, up, neworg); + VectorMA(neworg, movevars.stepheight*-2, up, end); + + trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent); + + if (trace.allsolid) + return false; + + if (trace.startsolid) + { + //move up by an extra step, if needed + VectorMA(neworg, -movevars.stepheight, up, neworg); + trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent); + if (trace.allsolid || trace.startsolid) + return false; + } + if (trace.fraction == 1) + { + // if monster had the ground pulled out, go ahead and fall + if ( (int)eflags & FL_PARTIALGROUND ) + { + if (domove) + { + VectorAdd (ent->v.origin, move, ent->v.origin); + if (relink) + SVHL_LinkEdict (ent, true); + ent->v.flags = (int)eflags & ~FL_ONGROUND; + } +// Con_Printf ("fall down\n"); + return true; + } + + return false; // walked off an edge + } + +// check point traces down for dangling corners + if (domove) + VectorCopy (trace.endpos, ent->v.origin); + +/* if (!World_CheckBottom (world, ent, up)) + { + if ( (int)ent->v->flags & FL_PARTIALGROUND ) + { // entity had floor mostly pulled out from underneath it + // and is trying to correct + if (relink) + SVHL_LinkEdict (ent, true); + return true; + } + + if (domove) + VectorCopy (oldorg, ent->v->origin); + return false; + } +*/ + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { +// Con_Printf ("back on ground\n"); + ent->v.flags &= ~FL_PARTIALGROUND; + } + ent->v.groundentity = trace.ent; + +// the move is ok + if (relink) + SVHL_LinkEdict (ent, true); + return true; } void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg) { @@ -470,12 +766,14 @@ void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg) void QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch) { bi_trace(); - SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch); + if (*soundname == '!') + return; //would need us to parse sound/sentances.txt I guess + SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch, 0, 0); } void QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch) { bi_trace(); - SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, 0); + SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, pitch, 0, 0); } void QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result) { @@ -522,7 +820,7 @@ char *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end) { trace_t tr; bi_trace(); - sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, 0, NULL, start, end, vec3_origin, vec3_origin, MASK_WORLDSOLID, &tr); + sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, 0, NULL, start, end, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr); return tr.surface->name; } unk QDECL GHL_TraceSphere(unk){notimpf(__func__);} @@ -530,6 +828,8 @@ unk QDECL GHL_GetAimVector(unk){notimpf(__func__);} void QDECL GHL_ServerCommand(char *cmd) { bi_trace(); + if (!strcmp(cmd, "reload\n")) + cmd = "restart\n"; Cbuf_AddText(cmd, RESTRICT_PROGS); } void QDECL GHL_ServerExecute(void) @@ -541,8 +841,9 @@ unk QDECL GHL_ClientCommand(unk){notimpf(__func__);} unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);} void QDECL GHL_LightStyle(int stylenum, char *stylestr) { + vec3_t rgb = {1,1,1}; bi_trace(); - PF_applylightstyle(stylenum, stylestr, 7); + PF_applylightstyle(stylenum, stylestr, rgb); } int QDECL GHL_DecalIndex(char *decalname) { @@ -619,6 +920,9 @@ void QDECL GHL_MessageEnd(unk) case MSG_MULTICAST+1: SV_Multicast(svhl_messageorigin, MULTICAST_PHS); break; + case 9: + //spectators only + break; default: Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest); break; @@ -629,7 +933,7 @@ void QDECL GHL_MessageEnd(unk) void QDECL GHL_WriteByte(int value) { bi_trace(); - MSG_WriteByte(&sv.multicast, value); + MSG_WriteByte(&sv.multicast, value & 0xff); } void QDECL GHL_WriteChar(int value) { @@ -730,7 +1034,102 @@ int QDECL GHL_RegUserMsg(char *msgname, int msgsize) return lastusermessage--; } unk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);} -unk QDECL GHL_GetBonePosition(unk){notimpf(__func__);} + +static void GHL_GetFrameState(hledict_t *ent, framestate_t *fstate) +{ + memset(fstate, 0, sizeof(*fstate)); + + fstate->g[FS_REG].frametime[0] = (SVHL_Globals.time - ent->v.framestarttime) * ent->v.framerate; + fstate->g[FS_REG].frame[0] = ent->v.frame; + fstate->g[FS_REG].lerpweight[0] = 1; + fstate->g[FS_REG].subblendfrac = ent->v.blending[0]; //fixme: which is upper? + + //FIXME: no lower parts. + + fstate->bonecontrols[0] = ent->v.controller[0] / 255.0; + fstate->bonecontrols[1] = ent->v.controller[1] / 255.0; + fstate->bonecontrols[2] = ent->v.controller[2] / 255.0; + fstate->bonecontrols[3] = ent->v.controller[3] / 255.0; +} +static void bonemat_fromqcvectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3]) +{ + out[0] = vx[0]; + out[1] = -vy[0]; + out[2] = vz[0]; + out[3] = t[0]; + out[4] = vx[1]; + out[5] = -vy[1]; + out[6] = vz[1]; + out[7] = t[1]; + out[8] = vx[2]; + out[9] = -vy[2]; + out[10] = vz[2]; + out[11] = t[2]; +} +static void bonemat_fromidentity(float *out) +{ + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; +} +static void bonemat_toqcvectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3]) +{ + vx[0] = in[0]; + vx[1] = in[4]; + vx[2] = in[8]; + vy[0] = -in[1]; + vy[1] = -in[5]; + vy[2] = -in[9]; + vz[0] = in[2]; + vz[1] = in[6]; + vz[2] = in[10]; + t [0] = in[3]; + t [1] = in[7]; + t [2] = in[11]; +} +static void bonemat_fromhlentity(hledict_t *ed, float *trans) +{ + vec3_t d[3], a; + a[0] = -ed->v.angles[0]; + a[1] = ed->v.angles[1]; + a[2] = ed->v.angles[2]; + AngleVectors(a, d[0], d[1], d[2]); + bonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v.origin); +} +void QDECL GHL_GetBonePosition(hledict_t *ed, int bone, vec3_t org, vec3_t ang) +{ + float transent[12]; + float transforms[12]; + float result[12]; + model_t *mod = sv.models[ed->v.modelindex]; + vec3_t axis[3]; + framestate_t fstate; + GHL_GetFrameState(ed, &fstate); + + bone += 1; //I *think* the bones are 0-based unlike our tag-based bone numbers + + if (!Mod_GetTag(mod, bone, &fstate, transforms)) + { + bonemat_fromidentity(transforms); + } + + bonemat_fromhlentity(ed, transent); + R_ConcatTransforms((void*)transent, (void*)transforms, (void*)result); + + bonemat_toqcvectors(result, axis[0], axis[1], axis[2], org); + VectorAngles(axis[0], axis[2], ang); +} hlintptr_t QDECL GHL_FunctionFromName(char *name) { @@ -772,7 +1171,7 @@ int QDECL GHL_Cmd_Argc(unk) unk QDECL GHL_GetAttachment(unk){notimpf(__func__);} void QDECL GHL_CRC32_Init(hlcrc_t *crc) { - unsigned short crc16 = *crc; + unsigned short crc16; bi_trace(); QCRC_Init(&crc16); *crc = crc16; @@ -1324,11 +1723,12 @@ void SV_ReadLibListDotGam(void) char value[1024]; char *file; char *s; + size_t fsize; Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info)); Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info)); - file = COM_LoadTempFile("liblist.gam"); + file = COM_LoadTempFile("liblist.gam", &fsize); if (!file) return; @@ -1368,6 +1768,8 @@ int SVHL_InitGame(void) {NULL, NULL} }; + memset(&SVHL_Globals, 0, sizeof(SVHL_Globals)); + if (sizeof(long) != sizeof(void*)) { Con_Printf("sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\n"); @@ -1387,7 +1789,8 @@ int SVHL_InitGame(void) gamedll = Info_ValueForKey(svs.info, "*gamedll"); iterator = NULL; - while(COM_IteratePaths(&iterator, path, sizeof(path))) + //FIXME: game dlls from game paths are evil/exploitable. + while(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0)) { snprintf (fullname, sizeof(fullname), "%s%s", path, gamedll); hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs); @@ -1404,7 +1807,7 @@ int SVHL_InitGame(void) if (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION)) { - Con_Printf(CON_ERROR "Error: %s is incompatible (FTE is compiled for %i)\n", fullname, HALFLIFE_API_VERSION); + Con_Printf(CON_ERROR "Error: %s is incompatible (Engine is compiled for %i)\n", fullname, HALFLIFE_API_VERSION); if (GetEntityAPI(&SVHL_GameFuncs, 138)) Con_Printf(CON_ERROR "mod is 138\n"); Sys_CloseLibrary(hlgamecode); @@ -1446,7 +1849,12 @@ void SVHL_SpawnEntities(char *entstring) SVHL_Globals.deathmatch = deathmatch.value; SVHL_Globals.coop = coop.value; SVHL_Globals.serverflags = 0; - SVHL_Globals.mapname = GHL_AllocString(sv.name); + if (!strncmp(sv.modelname, "maps/", 5)) + COM_StripExtension(sv.modelname+5, value, sizeof(value)); + else + COM_StripExtension(sv.modelname, value, sizeof(value)); + SVHL_Globals.mapname = GHL_AllocString(value); + SVHL_Globals.time = 0; SVHL_NumActiveEnts = 0; @@ -1474,8 +1882,12 @@ void SVHL_SpawnEntities(char *entstring) sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array for (i=1 ; inumsubmodels ; i++) { - sv.strings.model_precache[1+i] = localmodels[i]; - sv.models[i+1] = Mod_ForName (Mod_FixName(localmodels[i], sv.modelname), false); + const char *s = va("*%i", i); + char *n; + n = ZG_Malloc(&hlmapmemgroup, strlen(s)+1); + strcpy(n, s); + sv.strings.model_precache[1+i] = n; + sv.models[i+1] = Mod_ForName (Mod_FixName(n, sv.modelname), false); } while (entstring) @@ -1574,6 +1986,19 @@ qboolean HLSV_ClientCommand(client_t *client) hledict_t *ed = &SVHL_Edict[client - svs.clients + 1]; if (!hlgamecode) return false; + if (!strcmp("noclip", Cmd_Argv(0))) + { + if (ed->v.movetype != MOVETYPE_NOCLIP) + ed->v.movetype = MOVETYPE_NOCLIP; + else + ed->v.movetype = MOVETYPE_WALK; + return true; + } + if (!strcmp("kill", Cmd_Argv(0))) + { + SVHL_GameFuncs.ClientKill(ed); + return true; + } bi_begin(); SVHL_GameFuncs.ClientCommand(ed); bi_end(); @@ -1589,7 +2014,8 @@ qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[1 sv.skipbprintclient = client; bi_begin(); - result = SVHL_GameFuncs.ClientConnect(&SVHL_Edict[client-svs.clients+1], client->name, ipadr, rejectmessage); + client->hledict = &SVHL_Edict[client-svs.clients+1]; + result = SVHL_GameFuncs.ClientConnect(client->hledict, client->name, ipadr, rejectmessage); bi_end(); sv.skipbprintclient = NULL; @@ -1602,7 +2028,7 @@ void SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss) si[STAT_HEALTH] = ed->v.health; si[STAT_VIEWHEIGHT] = ed->v.view_ofs[2]; - si[STAT_WEAPON] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex); + si[STAT_WEAPONMODELI] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex); si[STAT_ITEMS] = ed->v.weapons; } @@ -1621,6 +2047,7 @@ void SVHL_DropClient(client_t *drop) bi_begin(); SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]); bi_end(); + drop->hledict = NULL; ed->isfree = true; } @@ -1636,8 +2063,9 @@ extern cvar_t temp1; usercmd_t cmd = *ucmd; cmd.msec = ucmd->msec/2; + cmd.msec_compat = floor(cmd.msec); SVHL_RunCmdR (ed, &cmd); - cmd.msec = ucmd->msec/2 + (ucmd->msec&1); //give them back their msec. + cmd.msec_compat = ucmd->msec - cmd.msec_compat; cmd.impulse = 0; SVHL_RunCmdR (ed, &cmd); return; @@ -1649,7 +2077,22 @@ extern cvar_t temp1; host_frametime = 0.1; pmove.cmd = *ucmd; - pmove.pm_type = temp1.value;//PM_NORMAL;//FLY; + switch(ed->v.movetype) + { + default: + case MOVETYPE_WALK: + pmove.pm_type = PM_NORMAL; + break; + case MOVETYPE_FLY: + pmove.pm_type = PM_FLY; + break; + case MOVETYPE_NOCLIP: + pmove.pm_type = PM_SPECTATOR; + break; + case MOVETYPE_NONE: + pmove.pm_type = PM_NONE; + break; + } pmove.numphysent = 1; pmove.physents[0].model = sv.world.worldmodel; pmove.physents[0].info = 0; @@ -1742,8 +2185,8 @@ extern cvar_t temp1; } } - VectorCopy(ed->v.mins, player_mins); - VectorCopy(ed->v.maxs, player_maxs); + VectorCopy(ed->v.mins, pmove.player_mins); + VectorCopy(ed->v.maxs, pmove.player_maxs); VectorCopy(ed->v.origin, pmove.origin); VectorCopy(ed->v.velocity, pmove.velocity); @@ -1823,6 +2266,8 @@ void SVHL_RunCmd(client_t *cl, usercmd_t *ucmd) ed->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]); ed->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]); + if (IS_NAN(ed->v.velocity[0]) || IS_NAN(ed->v.velocity[1]) || IS_NAN(ed->v.velocity[2])) + VectorClear(ed->v.velocity); bi_begin(); SVHL_GameFuncs.PlayerPreThink(ed); @@ -1890,12 +2335,43 @@ void SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs, s->number = i; s->modelindex = e->v.modelindex; s->frame = e->v.sequence1; - s->effects = e->v.effects; + s->effects = e->v.effects & 0x0f; + s->dpflags = 0; s->skinnum = e->v.skin; s->scale = 16; s->trans = 255; + s->colormod[0] = nullentitystate.colormod[0]; + s->colormod[1] = nullentitystate.colormod[1]; + s->colormod[2] = nullentitystate.colormod[2]; VectorCopy(e->v.angles, s->angles); VectorCopy(e->v.origin, s->origin); + + if (!e->v.velocity[0] && !e->v.velocity[1] && !e->v.velocity[2]) + s->dpflags |= RENDER_STEP; + + s->trans = e->v.renderamt*255; + switch (e->v.rendermode) + { + case 0: + s->trans = 255; + break; + case 1: //used on laser beams, apparently + break; + case 2: //transparent windows. + break; + case 3: //used on coronarey sprites. + s->effects |= NQEF_ADDITIVE; + break; + case 4: //used on fence textures, apparently. we already deal with these clientside. + s->trans = 255; + break; + case 5: //used on the torch at the start. + s->effects |= NQEF_ADDITIVE; + break; + default: + Con_Printf("Rendermode %s %i\n", SVHL_Globals.stringbase+e->v.model, e->v.rendermode); + break; + } } } diff --git a/engine/server/svhl_gcapi.h b/engine/server/svhl_gcapi.h index 054d18bb3..11d7f6fa1 100644 --- a/engine/server/svhl_gcapi.h +++ b/engine/server/svhl_gcapi.h @@ -417,7 +417,7 @@ typedef struct unk (QDECL *ChangeYaw)(unk); unk (QDECL *ChangePitch)(unk); hledict_t *(QDECL *FindEntityByString)(hledict_t *last, char *field, char *value); - unk (QDECL *GetEntityIllum)(unk); + int (QDECL *GetEntityIllum)(hledict_t *ent); hledict_t *(QDECL *FindEntityInSphere)(hledict_t *last, float *org, float radius); hledict_t *(QDECL *FindClientInPVS)(hledict_t *ed); unk (QDECL *EntitiesInPVS)(unk); @@ -479,7 +479,7 @@ typedef struct void *(QDECL *GetModelPtr)(hledict_t *ed); int (QDECL *RegUserMsg)(char *msgname, int msgsize); unk (QDECL *AnimationAutomove)(unk); - unk (QDECL *GetBonePosition)(unk); + void (QDECL *GetBonePosition)(hledict_t *ed, int bone, vec3_t org, vec3_t ang); hlintptr_t (QDECL *FunctionFromName)(char *name); char *(QDECL *NameForFunction)(hlintptr_t); unk (QDECL *ClientPrintf)(unk); diff --git a/engine/server/svhl_phys.c b/engine/server/svhl_phys.c index 0753f74ca..490321ddc 100644 --- a/engine/server/svhl_phys.c +++ b/engine/server/svhl_phys.c @@ -552,8 +552,8 @@ qboolean SVHL_PushAngles (hledict_t *pusher, vec3_t move, vec3_t amove) // see if the ent's bbox is inside the pusher's final position - // if (!SVHL_TestEntityPosition (check)) - // continue; + if (!SVHL_TestEntityPosition (check)) + continue; } if ((pusher->v.movetype == MOVETYPE_PUSH) || (check->v.groundentity == pusher)) @@ -886,7 +886,7 @@ Entities that are "stuck" to another entity */ void SVHL_Physics_Follow (hledict_t *ent) { - vec3_t vf, vr, vu, angles, v; +// vec3_t vf, vr, vu, angles, v; hledict_t *e; // regular thinking @@ -1817,13 +1817,16 @@ void SVHL_RunFrame (void) //only run physics tics if there's a client on the server. //this fixes the bug where the train moves out before the player spawns, so the player doesn't fall to his death - for (i = 0; i < sv.allocated_client_slots; i++) + if (sv.state == ss_active) { - if (svs.clients[i].state == cs_spawned) - break; + for (i = 0; i < sv.allocated_client_slots; i++) + { + if (svs.clients[i].state == cs_spawned) + break; + } + if (i == sv.allocated_client_slots) + return; } - if (i == sv.allocated_client_slots) - return; SVHL_Globals.frametime = host_frametime; diff --git a/engine/server/svhl_world.c b/engine/server/svhl_world.c index 36c2d67ff..ad7fcd59e 100644 --- a/engine/server/svhl_world.c +++ b/engine/server/svhl_world.c @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "svhl_gcapi.h" hull_t *World_HullForBox (vec3_t mins, vec3_t maxs); -qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask); +//qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask); /* entities never clip against themselves, or their owner @@ -380,12 +380,12 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t if (ent->v.solid != SOLID_BSP) { ent->v.angles[0]*=-1; //carmack made bsp models rotate wrongly. - TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask); + World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask); ent->v.angles[0]*=-1; } else { - TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask); + World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask); } // fix trace up by the offset @@ -396,7 +396,7 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t //okay, we hit the bbox model_t *model; - if (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_MODELS) + if (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_PRECACHE_MODELS) SV_Error("SV_ClipMoveToEntity: modelindex out of range\n"); model = sv.models[ (int)ent->v.modelindex ]; if (!model) @@ -408,7 +408,7 @@ trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t if (model && model->funcs.NativeTrace) { //do the second trace - TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID); + World_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID); } } diff --git a/engine/server/world.c b/engine/server/world.c index 69fe8957e..3f7b79e71 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -2097,7 +2097,7 @@ qboolean QDECL World_RegisterPhysicsEngine(const char *enginename, void(QDECL*st world_current_physics_engine = startupfunc; return true; } -static void World_ShutdownPhysics(world_t *world) +void World_RBE_Shutdown(world_t *world) { unsigned int u; wedict_t *ed; @@ -2124,11 +2124,11 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename) #if defined(CSQC_DAT) && !defined(SERVERONLY) { extern world_t csqc_world; - World_ShutdownPhysics(&csqc_world); + World_RBE_Shutdown(&csqc_world); } #endif #if !defined(CLIENTONLY) - World_ShutdownPhysics(&sv.world); + World_RBE_Shutdown(&sv.world); #endif world_current_physics_engine = NULL; @@ -2136,12 +2136,15 @@ void QDECL World_UnregisterPhysicsEngine(const char *enginename) void World_RBE_Start(world_t *world) { if (world_current_physics_engine) - world_current_physics_engine(world); + { + if (world->worldmodel) + world_current_physics_engine(world); + } } void World_Destroy(world_t *world) { - World_ShutdownPhysics(world); + World_RBE_Shutdown(world); Z_Free(world->areanodes); world->areanodes = NULL; diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c index 533f01da0..a6c15c296 100644 --- a/engine/sw/sw_backend.c +++ b/engine/sw/sw_backend.c @@ -375,7 +375,6 @@ void SWBE_SelectMode(backendmode_t mode) void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh) { - extern cvar_t temp1; int i; vecV_t *xyz; diff --git a/quakec/csaddon/src/editor_brushes.qc b/quakec/csaddon/src/editor_brushes.qc index 3bd33abce..74e81b911 100644 --- a/quakec/csaddon/src/editor_brushes.qc +++ b/quakec/csaddon/src/editor_brushes.qc @@ -110,7 +110,8 @@ int(int mod, brushface_t *faces, int numfaces, int contents) brush_history_creat h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); - history++; + if (h->id) + history++; history_max = history; //always break any pending redos if (history_min < history_max - historyring.length) history_min = history_max - historyring.length; @@ -941,6 +942,75 @@ void(brushface_t *faces, int numfaces) DebrushifyLite = } }; +void(vector org, vector ang) editor_brushes_simpleclone = +{ + vector midpoint; + if (!selectedbrush) + return; + + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + if (ang != '0 0 0') + { + brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1); + brushface_translate(tmp_faces, tmp_numfaces, -midpoint); + makevectors(ang); + brushface_rotate(tmp_faces, tmp_numfaces); + brushface_translate(tmp_faces, tmp_numfaces, midpoint + org); + } + else + brushface_translate(tmp_faces, tmp_numfaces, org); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); +}; + +void() brushedit_subtract = +{ + int discard; + vector selnormals[tmp_faces.length]; + float seldists[tmp_faces.length]; + + int planecount = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + for (int i = 0; i < planecount; i++) + { + selnormals[i] = tmp_faces[i].planenormal; + seldists[i] = tmp_faces[i].planedist; + } + int numbrushes = brush_findinvolume(selectedbrushmodel, selnormals, seldists, planecount, brushlist, __NULL__, brushlist.length); + + while (numbrushes --> 0) + { + int br = brushlist[numbrushes]; + if (br == selectedbrush) + continue; + + int counto = brush_get(selectedbrushmodel, br, tmp_faces, tmp_faces.length, &tmp_contents); + int counts = counto + brush_get(selectedbrushmodel, selectedbrush, tmp_faces+counto, tmp_faces.length-counto, &discard); + + brush_history_delete(selectedbrushmodel, br); + while(counts --> counto) + { + //only consider the resulting brush if the new face actually contributed anything. + //this reduces dupes. + if (brush_calcfacepoints(1+counts, tmp_faces, counts+1, facepoints, facepoints.length)) + { + //determine the brush defined by this plane + tmp_faces[counts].planenormal *= -1; + tmp_faces[counts].planedist *= -1; + brush_history_create(selectedbrushmodel, tmp_faces, counts+1, tmp_contents); + } + } + } +}; + +void() brushedit_resettextures = +{ + int planecount = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + for (int i = 0; i < planecount; i++) + reset_texturecoords(&tmp_faces[i]); + + brush_history_delete(selectedbrushmodel, selectedbrush); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, planecount, tmp_contents); +}; + void(vector mousepos) editor_brushes_add = { vector col = '0 0 0'; @@ -1482,29 +1552,12 @@ brusheditormodes registercommand("brushedit_matchface"); registercommand("brushedit_resettexcoords"); registercommand("brushedit_settexture"); + registercommand("brushedit_subtract"); registercommand("brushedit_binds"); registercommand("+brushedit_nogrid"); registercommand("-brushedit_nogrid"); }; -void(vector org, vector ang) editor_brushes_simpleclone = -{ - vector midpoint; - if (!selectedbrush) - return; - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); - if (ang != '0 0 0') - { - brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1); - brushface_translate(tmp_faces, tmp_numfaces, -midpoint); - makevectors(ang); - brushface_rotate(tmp_faces, tmp_numfaces); - brushface_translate(tmp_faces, tmp_numfaces, midpoint + org); - } - else - brushface_translate(tmp_faces, tmp_numfaces, org); - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); -}; float() editor_brushes_command = { switch(argv(0)) @@ -1537,15 +1590,16 @@ brusheditormodes bt_points = 0; break; case "brushedit_resettexcoords": - //IMPLEMENTME - brushtool = BT_NONE; - bt_points = 0; + brushedit_resettextures(); break; case "brushedit_settexture": //IMPLEMENTME brushtool = BT_NONE; bt_points = 0; break; + case "brushedit_subtract": + brushedit_subtract(); + break; case "brushedit_delete": if (selectedbrushmodel && selectedbrush) brush_history_delete(selectedbrushmodel, selectedbrush); @@ -1553,15 +1607,16 @@ brusheditormodes break; case "brushedit_binds": localcmd("conmenu \"\"\n"); - localcmd("menubind 0 8 \"brushedit_create\" \"Creation Tool\"\n"); - localcmd("menubind 0 16 \"brushedit_slice\" \"Slice Tool\"\n"); - localcmd("menubind 0 24 \"brushedit_delete\" \"Delete\"\n"); - localcmd("menubind 0 32 \"brushedit_undo\" \"Undo\"\n"); - localcmd("menubind 0 40 \"brushedit_redo\" \"Redo\"\n"); - localcmd("menubind 0 48 \"brushedit_nogrid\" \"Disable Grid\"\n"); + float foo = 0; + localcmd(sprintf("menubind 0 %g \"brushedit_create\" \"Creation Tool\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_slice\" \"Slice Tool\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_delete\" \"Delete\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_subtract\" \"Subtract\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_undo\" \"Undo\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_redo\" \"Redo\"\n", foo+=8)); + localcmd(sprintf("menubind 0 %g \"brushedit_nogrid\" \"Disable Grid\"\n", foo+=8)); - float foo = 56; -#define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo)); foo+=8; +#define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo+=8)); brusheditormodes #undef brusheditormode break; @@ -1804,11 +1859,5 @@ float(float key, float unic, vector mousepos) editor_brushes_key = return TRUE; } -/* if (key == 's') - { - //CSG subtraction is actually quite easy... - //for each brush that intersects us, split it by every single one of our planes that intesect - //drop the resulting brushes if they contain contain points only within the subtraction region - } -*/ return FALSE; + return FALSE; }; diff --git a/specs/particles.txt b/specs/particles.txt index 146bbcb56..e33e0acbb 100644 --- a/specs/particles.txt +++ b/specs/particles.txt @@ -133,6 +133,10 @@ inwater Specifies a replacement effect to use when this one is spawned underwater. assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains). +overwater + specifies that this +underwater + colorindex [rand] Specifies a palette index to spawn the particle with. The index used is between index and index+rand. @@ -279,6 +283,10 @@ emitstart spawnorg [vert] spawnvel [vert] +viewspace [frac] + Specifies that this particle type should move relative to the camera. + Not compatible with splitscreen. + perframe apply inverse frametime to count (causes emits to be per frame)