mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-27 14:12:25 +00:00
d396450a0a
fixed numerous shadowmapping bugs. actually seems to work now. appears to draw more lights than is actually needed, however. changed how keyboard focus works. can now have menu+console open at once, although you probably need shift+escape to get at it. fixed a few issues with nexuiz compat. there are *still* other issues. greatly refactored cd playback code. cd driver code is now a backend only and does not provide its own commands. track remapping accepts named faketracks. worked around missing notifications in vista+, so looping will now work. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4491 fc73d0e0-1445-4013-8a0c-d673dee63da5
469 lines
12 KiB
C
469 lines
12 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the included (GNU.txt) GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
#include "quakedef.h"
|
|
|
|
#define NUMVERTEXNORMALS 162
|
|
float r_avertexnormals[NUMVERTEXNORMALS][3] = {
|
|
#include "anorms.h"
|
|
};
|
|
|
|
|
|
void R_Rockettrail_Callback(struct cvar_s *var, char *oldvalue)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
extern model_t mod_known[];
|
|
extern int mod_numknown;
|
|
|
|
if (cls.state == ca_disconnected)
|
|
return; // don't bother parsing while disconnected
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
{
|
|
if (!mod->needload)
|
|
if (mod->flags & MF_ROCKET)
|
|
P_DefaultTrail(mod);
|
|
}
|
|
}
|
|
|
|
void R_Grenadetrail_Callback(struct cvar_s *var, char *oldvalue)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
extern model_t mod_known[];
|
|
extern int mod_numknown;
|
|
|
|
if (cls.state == ca_disconnected)
|
|
return; // don't bother parsing while disconnected
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
{
|
|
if (!mod->needload)
|
|
if (mod->flags & MF_GRENADE)
|
|
P_DefaultTrail(mod);
|
|
}
|
|
}
|
|
|
|
extern particleengine_t pe_null;
|
|
#ifdef PSET_CLASSIC
|
|
extern particleengine_t pe_classic;
|
|
#endif
|
|
particleengine_t pe_darkplaces;
|
|
particleengine_t pe_qmb;
|
|
#ifdef PSET_SCRIPT
|
|
extern particleengine_t pe_script;
|
|
#endif
|
|
|
|
particleengine_t *particlesystem[] =
|
|
{
|
|
#ifdef PSET_SCRIPT
|
|
&pe_script,
|
|
#endif
|
|
&pe_darkplaces,
|
|
&pe_qmb,
|
|
#ifdef PSET_CLASSIC
|
|
&pe_classic,
|
|
#endif
|
|
&pe_null,
|
|
NULL,
|
|
};
|
|
|
|
void R_ParticleSystem_Callback(struct cvar_s *var, char *oldvalue)
|
|
{
|
|
int i;
|
|
if (pe)
|
|
{
|
|
CL_ClearTEntParticleState();
|
|
CL_ClearLerpEntsParticleState();
|
|
#ifdef Q2CLIENT
|
|
CLQ2_ClearParticleState();
|
|
#endif
|
|
|
|
pe->ShutdownParticles();
|
|
}
|
|
|
|
if (!qrenderer)
|
|
{
|
|
pe = &pe_null;
|
|
}
|
|
else
|
|
{
|
|
pe = NULL;
|
|
for (i = 0; particlesystem[i]; i++)
|
|
{
|
|
if ( (particlesystem[i]->name1 && !stricmp(var->string, particlesystem[i]->name1))
|
|
|| (particlesystem[i]->name2 && !stricmp(var->string, particlesystem[i]->name2)))
|
|
{
|
|
pe = particlesystem[i];
|
|
break;
|
|
}
|
|
if (!pe)
|
|
if (particlesystem[i]->name1)
|
|
pe = particlesystem[i];
|
|
}
|
|
}
|
|
if (!pe)
|
|
Sys_Error("No particle system available. Please recompile.");
|
|
|
|
if (!pe->InitParticles())
|
|
{
|
|
Con_Printf("Particlesystem %s failed to init\n", pe->name1);
|
|
pe = &pe_null;
|
|
pe->InitParticles();
|
|
}
|
|
pe->ClearParticles();
|
|
CL_RegisterParticles();
|
|
}
|
|
|
|
cvar_t r_rockettrail = CVARFC("r_rockettrail", "1", CVAR_SEMICHEAT, R_Rockettrail_Callback);
|
|
cvar_t r_grenadetrail = CVARFC("r_grenadetrail", "1", CVAR_SEMICHEAT, R_Grenadetrail_Callback);
|
|
cvar_t r_particlesystem = CVARFC("r_particlesystem", IFMINIMAL("classic", "script"), CVAR_SEMICHEAT|CVAR_ARCHIVE, R_ParticleSystem_Callback);
|
|
cvar_t r_particledesc = CVARAF("r_particledesc", "classic", "r_particlesdesc", CVAR_SEMICHEAT|CVAR_ARCHIVE);
|
|
extern cvar_t r_bouncysparks;
|
|
extern cvar_t r_part_rain;
|
|
extern cvar_t r_bloodstains;
|
|
extern cvar_t gl_part_flame;
|
|
cvar_t r_part_rain_quantity = CVARF("r_part_rain_quantity", "1", CVAR_ARCHIVE);
|
|
|
|
cvar_t r_particle_tracelimit = CVARFD("r_particle_tracelimit", "200", CVAR_ARCHIVE, "Number of traces to allow per frame for particle physics.");
|
|
cvar_t r_part_sparks = CVAR("r_part_sparks", "1");
|
|
cvar_t r_part_sparks_trifan = CVAR("r_part_sparks_trifan", "1");
|
|
cvar_t r_part_sparks_textured = CVAR("r_part_sparks_textured", "1");
|
|
cvar_t r_part_beams = CVAR("r_part_beams", "1");
|
|
cvar_t r_part_contentswitch = CVARFD("r_part_contentswitch", "1", CVAR_ARCHIVE, "Enable particle effects to change based on content (ex. water).");
|
|
cvar_t r_part_density = CVARF("r_part_density", "1", CVAR_ARCHIVE);
|
|
|
|
|
|
particleengine_t *pe;
|
|
|
|
void P_InitParticleSystem(void)
|
|
{
|
|
char *particlecvargroupname = "Particle effects";
|
|
|
|
Cvar_Register(&r_particlesystem, "Particles");
|
|
|
|
//particles
|
|
Cvar_Register(&r_particledesc, particlecvargroupname);
|
|
Cvar_Register(&r_bouncysparks, particlecvargroupname);
|
|
Cvar_Register(&r_part_rain, particlecvargroupname);
|
|
|
|
Cvar_Register(&r_part_rain_quantity, particlecvargroupname);
|
|
|
|
Cvar_Register(&r_particle_tracelimit, particlecvargroupname);
|
|
|
|
Cvar_Register(&r_part_sparks, particlecvargroupname);
|
|
Cvar_Register(&r_part_sparks_trifan, particlecvargroupname);
|
|
Cvar_Register(&r_part_sparks_textured, particlecvargroupname);
|
|
Cvar_Register(&r_part_beams, particlecvargroupname);
|
|
Cvar_Register(&r_part_contentswitch, particlecvargroupname);
|
|
Cvar_Register(&r_part_density, particlecvargroupname);
|
|
|
|
Cvar_Register (&gl_part_flame, particlecvargroupname);
|
|
|
|
Cvar_Register (&r_rockettrail, particlecvargroupname);
|
|
Cvar_Register (&r_grenadetrail, particlecvargroupname);
|
|
}
|
|
|
|
void P_Shutdown(void)
|
|
{
|
|
if (pe)
|
|
{
|
|
CL_ClearLerpEntsParticleState();
|
|
pe->ShutdownParticles();
|
|
}
|
|
pe = NULL;
|
|
}
|
|
|
|
#ifdef Q2BSPS
|
|
qboolean Q2TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
|
|
{
|
|
vec3_t nul = {0,0,0};
|
|
trace_t trace = CM_BoxTrace(pmove.physents[0].model, start, end, nul, nul, MASK_WORLDSOLID);
|
|
|
|
if (trace.fraction < 1)
|
|
{
|
|
VectorCopy (trace.plane.normal, normal);
|
|
VectorCopy (trace.endpos, impact);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
|
|
{
|
|
trace_t trace;
|
|
float len, bestlen;
|
|
int i;
|
|
vec3_t delta, ts, te;
|
|
physent_t *pe;
|
|
qboolean clipped=false;
|
|
vec3_t axis[3];
|
|
|
|
memset (&trace, 0, sizeof(trace));
|
|
|
|
VectorSubtract(end, start, delta);
|
|
bestlen = Length(delta);
|
|
|
|
VectorCopy (end, impact);
|
|
|
|
for (i=0 ; i< pmove.numphysent ; i++)
|
|
{
|
|
pe = &pmove.physents[i];
|
|
if (pe->nonsolid)
|
|
continue;
|
|
if (pe->model && !pe->model->needload)
|
|
{
|
|
VectorSubtract(start, pe->origin, ts);
|
|
VectorSubtract(end, pe->origin, te);
|
|
if (pe->angles[0] || pe->angles[1] || pe->angles[2])
|
|
{
|
|
AngleVectors(pe->angles, axis[0], axis[1], axis[2]);
|
|
VectorNegate(axis[1], axis[1]);
|
|
pe->model->funcs.NativeTrace(pe->model, 0, 0, axis, ts, te, vec3_origin, vec3_origin, MASK_WORLDSOLID, &trace);
|
|
}
|
|
else
|
|
pe->model->funcs.NativeTrace(pe->model, 0, 0, NULL, ts, te, vec3_origin, vec3_origin, MASK_WORLDSOLID, &trace);
|
|
if (trace.fraction<1)
|
|
{
|
|
VectorSubtract(trace.endpos, ts, delta);
|
|
len = Length(delta);
|
|
if (len < bestlen)
|
|
{
|
|
bestlen = len;
|
|
if (normal)
|
|
VectorCopy (trace.plane.normal, normal);
|
|
VectorAdd (pe->origin, trace.endpos, impact);
|
|
}
|
|
|
|
clipped=true;
|
|
}
|
|
if (trace.startsolid)
|
|
{
|
|
VectorNormalize(delta);
|
|
if (normal)
|
|
{
|
|
normal[0] = -delta[0];
|
|
normal[1] = -delta[1];
|
|
normal[2] = -delta[2];
|
|
}
|
|
VectorCopy (end, impact);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (clipped)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//handy utility...
|
|
void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk)
|
|
{
|
|
if (cl.paused)
|
|
return;
|
|
|
|
pe->RunParticleEffectState(pos, NULL, ((host_frametime>0.1)?0.1:host_frametime), type, tsk);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// P_SelectableTrail: given default/opposite effects, model pointer, and a user selection cvar
|
|
// changes model to the appropriate trail effect and default trail index
|
|
void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdlcidx, int oppeffect, int oppcidx)
|
|
{
|
|
int select = (int)(selection->value);
|
|
|
|
switch (select)
|
|
{
|
|
case 0: // check for string, otherwise no trail
|
|
if (selection->string[0] == '0')
|
|
{
|
|
model->particletrail = P_INVALID;
|
|
model->traildefaultindex = -1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
int effect = P_FindParticleType(selection->string);
|
|
|
|
if (effect >= 0)
|
|
{
|
|
model->particletrail = effect;
|
|
model->traildefaultindex = mdlcidx;
|
|
break;
|
|
}
|
|
}
|
|
// fall through to default (so semicheat will work properly)
|
|
case 1: // default model effect
|
|
default:
|
|
model->particletrail = mdleffect;
|
|
model->traildefaultindex = mdlcidx;
|
|
break;
|
|
case 2: // opposite effect
|
|
model->particletrail = oppeffect;
|
|
model->traildefaultindex= oppcidx;
|
|
break;
|
|
case 3: // alt rocket effect
|
|
model->particletrail = P_FindParticleType("TR_ALTROCKET");
|
|
model->traildefaultindex = 107;
|
|
break;
|
|
case 4: // gib
|
|
model->particletrail = P_FindParticleType("TR_BLOOD");
|
|
model->traildefaultindex = 70;
|
|
break;
|
|
case 5: // zombie gib
|
|
model->particletrail = P_FindParticleType("TR_SLIGHTBLOOD");
|
|
model->traildefaultindex = 70;
|
|
break;
|
|
case 6: // Scrag tracer
|
|
model->particletrail = P_FindParticleType("TR_WIZSPIKE");
|
|
model->traildefaultindex = 60;
|
|
break;
|
|
case 7: // Knight tracer
|
|
model->particletrail = P_FindParticleType("TR_KNIGHTSPIKE");
|
|
model->traildefaultindex = 238;
|
|
break;
|
|
case 8: // Vore tracer
|
|
model->particletrail = P_FindParticleType("TR_VORESPIKE");
|
|
model->traildefaultindex = 154;
|
|
break;
|
|
case 9: // rail trail
|
|
model->particletrail = P_FindParticleType("TE_RAILTRAIL");
|
|
model->traildefaultindex = 15;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//figure out which particle trail to use for the given model, filling in its values as required.
|
|
void P_DefaultTrail (model_t *model)
|
|
{
|
|
// TODO: EF_BRIGHTFIELD should probably be handled in here somewhere
|
|
// TODO: make trail default color into RGB values instead of indexes
|
|
if (model->engineflags & MDLF_NODEFAULTTRAIL)
|
|
return;
|
|
|
|
if (model->flags & MF_ROCKET)
|
|
P_SelectableTrail(model, &r_rockettrail, P_FindParticleType("TR_ROCKET"), 109, P_FindParticleType("TR_GRENADE"), 6);
|
|
else if (model->flags & MF_GRENADE)
|
|
P_SelectableTrail(model, &r_grenadetrail, P_FindParticleType("TR_GRENADE"), 6, P_FindParticleType("TR_ROCKET"), 109);
|
|
else if (model->flags & MF_GIB)
|
|
{
|
|
model->particletrail = P_FindParticleType("TR_BLOOD");
|
|
model->traildefaultindex = 70;
|
|
}
|
|
else if (model->flags & MF_TRACER)
|
|
{
|
|
model->particletrail = P_FindParticleType("TR_WIZSPIKE");
|
|
model->traildefaultindex = 60;
|
|
}
|
|
else if (model->flags & MF_ZOMGIB)
|
|
{
|
|
model->particletrail = P_FindParticleType("TR_SLIGHTBLOOD");
|
|
model->traildefaultindex = 70;
|
|
}
|
|
else if (model->flags & MF_TRACER2)
|
|
{
|
|
model->particletrail = P_FindParticleType("TR_KNIGHTSPIKE");
|
|
model->traildefaultindex = 238;
|
|
}
|
|
else if (model->flags & MF_TRACER3)
|
|
{
|
|
model->particletrail = P_FindParticleType("TR_VORESPIKE");
|
|
model->traildefaultindex = 154;
|
|
}
|
|
else if (model->flags & MFH2_BLOODSHOT) //these are the hexen2 ones.
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_bloodshot");
|
|
model->traildefaultindex = 136;
|
|
}
|
|
else if (model->flags & MFH2_FIREBALL)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_fireball");
|
|
model->traildefaultindex = 424;
|
|
}
|
|
else if (model->flags & MFH2_ACIDBALL)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_acidball");
|
|
model->traildefaultindex = 440;
|
|
}
|
|
else if (model->flags & MFH2_ICE)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_ice");
|
|
model->traildefaultindex = 408;
|
|
}
|
|
else if (model->flags & MFH2_SPIT)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_spit");
|
|
model->traildefaultindex = 260;
|
|
}
|
|
else if (model->flags & MFH2_SPELL)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_spell");
|
|
model->traildefaultindex = 260;
|
|
}
|
|
else if (model->flags & MFH2_VORP_MISSILE)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_vorpmissile");
|
|
model->traildefaultindex = 302;
|
|
}
|
|
else if (model->flags & MFH2_SET_STAFF)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_setstaff");
|
|
model->traildefaultindex = 424;
|
|
}
|
|
else if (model->flags & MFH2_MAGICMISSILE)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_magicmissile");
|
|
model->traildefaultindex = 149;
|
|
}
|
|
else if (model->flags & MFH2_BONESHARD)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_boneshard");
|
|
model->traildefaultindex = 384;
|
|
}
|
|
else if (model->flags & MFH2_SCARAB)
|
|
{
|
|
model->particletrail = P_FindParticleType("tr_scarab");
|
|
model->traildefaultindex = 254;
|
|
}
|
|
else
|
|
{
|
|
model->particletrail = P_INVALID;
|
|
model->traildefaultindex = -1;
|
|
}
|
|
}
|