yquake2remaster/src/client/cl_view.c
Yamagi Burmeister 61400d1ae8 Move input.c and input.h into the client and rename to sdl.c.
The input system backend was once used in the client and the renderers,
but for some years now it has been an integral part of the client only.
Move it there.
2018-08-07 09:43:34 +02:00

683 lines
14 KiB
C

/*
* Copyright (C) 1997-2001 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 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.
*
* =======================================================================
*
* This file implements the camera, e.g the player's view
*
* =======================================================================
*/
#include "header/client.h"
#include "input/header/input.h"
/* development tools for weapons */
int gun_frame;
struct model_s *gun_model;
cvar_t *crosshair;
cvar_t *crosshair_3d;
cvar_t *crosshair_3d_glow;
cvar_t *crosshair_scale;
cvar_t *cl_testparticles;
cvar_t *cl_testentities;
cvar_t *cl_testlights;
cvar_t *cl_testblend;
cvar_t *crosshair_3d_glow_r;
cvar_t *crosshair_3d_glow_g;
cvar_t *crosshair_3d_glow_b;
cvar_t *cl_stats;
int r_numdlights;
dlight_t r_dlights[MAX_DLIGHTS];
int r_numentities;
entity_t r_entities[MAX_ENTITIES];
int r_numparticles;
particle_t r_particles[MAX_PARTICLES];
lightstyle_t r_lightstyles[MAX_LIGHTSTYLES];
char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
int num_cl_weaponmodels;
void V_Render3dCrosshair(void);
/*
* Specifies the model that will be used as the world
*/
void
V_ClearScene(void)
{
r_numdlights = 0;
r_numentities = 0;
r_numparticles = 0;
}
void
V_AddEntity(entity_t *ent)
{
if (r_numentities >= MAX_ENTITIES)
{
return;
}
r_entities[r_numentities++] = *ent;
}
void
V_AddParticle(vec3_t org, unsigned int color, float alpha)
{
particle_t *p;
if (r_numparticles >= MAX_PARTICLES)
{
return;
}
p = &r_particles[r_numparticles++];
VectorCopy(org, p->origin);
p->color = color;
p->alpha = alpha;
}
void
V_AddLight(vec3_t org, float intensity, float r, float g, float b)
{
dlight_t *dl;
if (r_numdlights >= MAX_DLIGHTS)
{
return;
}
dl = &r_dlights[r_numdlights++];
VectorCopy(org, dl->origin);
dl->intensity = intensity;
dl->color[0] = r;
dl->color[1] = g;
dl->color[2] = b;
}
void
V_AddLightStyle(int style, float r, float g, float b)
{
lightstyle_t *ls;
if ((style < 0) || (style > MAX_LIGHTSTYLES))
{
Com_Error(ERR_DROP, "Bad light style %i", style);
}
ls = &r_lightstyles[style];
ls->white = r + g + b;
ls->rgb[0] = r;
ls->rgb[1] = g;
ls->rgb[2] = b;
}
/*
*If cl_testparticles is set, create 4096 particles in the view
*/
void
V_TestParticles(void)
{
particle_t *p;
int i, j;
float d, r, u;
r_numparticles = MAX_PARTICLES;
for (i = 0; i < r_numparticles; i++)
{
d = i * 0.25f;
r = 4 * ((i & 7) - 3.5f);
u = 4 * (((i >> 3) & 7) - 3.5f);
p = &r_particles[i];
for (j = 0; j < 3; j++)
{
p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * d +
cl.v_right[j] * r + cl.v_up[j] * u;
}
p->color = 8;
p->alpha = cl_testparticles->value;
}
}
/*
* If cl_testentities is set, create 32 player models
*/
void
V_TestEntities(void)
{
int i, j;
float f, r;
entity_t *ent;
r_numentities = 32;
memset(r_entities, 0, sizeof(r_entities));
for (i = 0; i < r_numentities; i++)
{
ent = &r_entities[i];
r = 64 * ((i % 4) - 1.5);
f = 64 * (i / 4) + 128;
for (j = 0; j < 3; j++)
{
ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f +
cl.v_right[j] * r;
}
ent->model = cl.baseclientinfo.model;
ent->skin = cl.baseclientinfo.skin;
}
}
/*
* If cl_testlights is set, create 32 lights models
*/
void
V_TestLights(void)
{
int i, j;
float f, r;
dlight_t *dl;
r_numdlights = 32;
memset(r_dlights, 0, sizeof(r_dlights));
for (i = 0; i < r_numdlights; i++)
{
dl = &r_dlights[i];
r = 64 * ((i % 4) - 1.5f);
f = 64 * (i / 4.0f) + 128;
for (j = 0; j < 3; j++)
{
dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f +
cl.v_right[j] * r;
}
dl->color[0] = (float)(((i % 6) + 1) & 1);
dl->color[1] = (float)((((i % 6) + 1) & 2) >> 1);
dl->color[2] = (float)((((i % 6) + 1) & 4) >> 2);
dl->intensity = 200;
}
}
/*
* Call before entering a new level, or after changing dlls
*/
void
CL_PrepRefresh(void)
{
char mapname[32];
int i;
char name[MAX_QPATH];
float rotate;
vec3_t axis;
if (!cl.configstrings[CS_MODELS + 1][0])
{
return;
}
SCR_AddDirtyPoint(0, 0);
SCR_AddDirtyPoint(viddef.width - 1, viddef.height - 1);
/* let the refresher load the map */
strcpy(mapname, cl.configstrings[CS_MODELS + 1] + 5); /* skip "maps/" */
mapname[strlen(mapname) - 4] = 0; /* cut off ".bsp" */
/* register models, pics, and skins */
Com_Printf("Map: %s\r", mapname);
SCR_UpdateScreen();
R_BeginRegistration (mapname);
Com_Printf(" \r");
/* precache status bar pics */
Com_Printf("pics\r");
SCR_UpdateScreen();
SCR_TouchPics();
Com_Printf(" \r");
CL_RegisterTEntModels();
num_cl_weaponmodels = 1;
strcpy(cl_weaponmodels[0], "weapon.md2");
for (i = 1; i < MAX_MODELS && cl.configstrings[CS_MODELS + i][0]; i++)
{
strcpy(name, cl.configstrings[CS_MODELS + i]);
name[37] = 0; /* never go beyond one line */
if (name[0] != '*')
{
Com_Printf("%s\r", name);
}
SCR_UpdateScreen();
IN_Update();
if (name[0] == '#')
{
/* special player weapon model */
if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
{
Q_strlcpy(cl_weaponmodels[num_cl_weaponmodels],
cl.configstrings[CS_MODELS + i] + 1,
sizeof(cl_weaponmodels[num_cl_weaponmodels]));
num_cl_weaponmodels++;
}
}
else
{
cl.model_draw[i] = R_RegisterModel(cl.configstrings[CS_MODELS + i]);
if (name[0] == '*')
{
cl.model_clip[i] = CM_InlineModel(cl.configstrings[CS_MODELS + i]);
}
else
{
cl.model_clip[i] = NULL;
}
}
if (name[0] != '*')
{
Com_Printf(" \r");
}
}
Com_Printf("images\r");
SCR_UpdateScreen();
for (i = 1; i < MAX_IMAGES && cl.configstrings[CS_IMAGES + i][0]; i++)
{
cl.image_precache[i] = Draw_FindPic(cl.configstrings[CS_IMAGES + i]);
IN_Update();
}
Com_Printf(" \r");
for (i = 0; i < MAX_CLIENTS; i++)
{
if (!cl.configstrings[CS_PLAYERSKINS + i][0])
{
continue;
}
Com_Printf("client %i\r", i);
SCR_UpdateScreen();
IN_Update();
CL_ParseClientinfo(i);
Com_Printf(" \r");
}
CL_LoadClientinfo(&cl.baseclientinfo, "unnamed\\male/grunt");
/* set sky textures and speed */
Com_Printf("sky\r");
SCR_UpdateScreen();
rotate = (float)strtod(cl.configstrings[CS_SKYROTATE], (char **)NULL);
sscanf(cl.configstrings[CS_SKYAXIS], "%f %f %f", &axis[0], &axis[1], &axis[2]);
R_SetSky(cl.configstrings[CS_SKY], rotate, axis);
Com_Printf(" \r");
/* the renderer can now free unneeded stuff */
R_EndRegistration();
/* clear any lines of console text */
Con_ClearNotify();
SCR_UpdateScreen();
cl.refresh_prepped = true;
cl.force_refdef = true; /* make sure we have a valid refdef */
#if defined(OGG)
int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10);
/* start the cd track */
if (Cvar_VariableValue("cd_shuffle"))
{
OGG_PlayTrack(track);
}
else
{
OGG_PlayTrack(track);
}
#endif
}
float
CalcFov(float fov_x, float width, float height)
{
float a;
float x;
if ((fov_x < 1) || (fov_x > 179))
{
Com_Error(ERR_DROP, "Bad fov: %f", fov_x);
}
x = width / (float)tan(fov_x / 360 * M_PI);
a = (float)atan(height / x);
a = a * 360 / M_PI;
return a;
}
/* gun frame debugging functions */
void
V_Gun_Next_f(void)
{
gun_frame++;
Com_Printf("frame %i\n", gun_frame);
}
void
V_Gun_Prev_f(void)
{
gun_frame--;
if (gun_frame < 0)
{
gun_frame = 0;
}
Com_Printf("frame %i\n", gun_frame);
}
void
V_Gun_Model_f(void)
{
char name[MAX_QPATH];
if (Cmd_Argc() != 2)
{
gun_model = NULL;
return;
}
Com_sprintf(name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
gun_model = R_RegisterModel(name);
}
int
entitycmpfnc(const entity_t *a, const entity_t *b)
{
/* all other models are sorted by model then skin */
if (a->model == b->model)
{
return (a->skin == b->skin) ? 0 :
(a->skin > b->skin) ? 1 : -1;
}
else
{
return (a->model == b->model) ? 0 :
(a->model > b->model) ? 1 : -1;
}
}
void
V_RenderView(float stereo_separation)
{
if (cls.state != ca_active)
{
return;
}
if (!cl.refresh_prepped)
{
return;
}
if (cl_timedemo->value)
{
if (!cl.timedemo_start)
{
cl.timedemo_start = Sys_Milliseconds();
}
cl.timedemo_frames++;
}
/* an invalid frame will just use the exact previous refdef
we can't use the old frame if the video mode has changed, though... */
if (cl.frame.valid && (cl.force_refdef || !cl_paused->value))
{
cl.force_refdef = false;
V_ClearScene();
/* build a refresh entity list and calc cl.sim*
this also calls CL_CalcViewValues which loads
v_forward, etc. */
CL_AddEntities();
// before changing viewport we should trace the crosshair position
V_Render3dCrosshair();
if (cl_testparticles->value)
{
V_TestParticles();
}
if (cl_testentities->value)
{
V_TestEntities();
}
if (cl_testlights->value)
{
V_TestLights();
}
if (cl_testblend->value)
{
cl.refdef.blend[0] = 1;
cl.refdef.blend[1] = 0.5;
cl.refdef.blend[2] = 0.25;
cl.refdef.blend[3] = 0.5;
}
/* offset vieworg appropriately if
we're doing stereo separation */
if (stereo_separation != 0)
{
vec3_t tmp;
VectorScale(cl.v_right, stereo_separation, tmp);
VectorAdd(cl.refdef.vieworg, tmp, cl.refdef.vieworg);
}
/* never let it sit exactly on a node line, because a water plane can
dissapear when viewed with the eye exactly on it. the server protocol
only specifies to 1/8 pixel, so add 1/16 in each axis */
cl.refdef.vieworg[0] += 1.0 / 16;
cl.refdef.vieworg[1] += 1.0 / 16;
cl.refdef.vieworg[2] += 1.0 / 16;
cl.refdef.time = cl.time * 0.001f;
cl.refdef.areabits = cl.frame.areabits;
if (!cl_add_entities->value)
{
r_numentities = 0;
}
if (!cl_add_particles->value)
{
r_numparticles = 0;
}
if (!cl_add_lights->value)
{
r_numdlights = 0;
}
if (!cl_add_blend->value)
{
VectorClear(cl.refdef.blend);
}
cl.refdef.num_entities = r_numentities;
cl.refdef.entities = r_entities;
cl.refdef.num_particles = r_numparticles;
cl.refdef.particles = r_particles;
cl.refdef.num_dlights = r_numdlights;
cl.refdef.dlights = r_dlights;
cl.refdef.lightstyles = r_lightstyles;
cl.refdef.rdflags = cl.frame.playerstate.rdflags;
/* sort entities for better cache locality */
qsort(cl.refdef.entities, cl.refdef.num_entities,
sizeof(cl.refdef.entities[0]), (int (*)(const void *, const void *))
entitycmpfnc);
} else if (cl.frame.valid && cl_paused->value && gl1_stereo->value) {
// We need to adjust the refdef in stereo mode when paused.
vec3_t tmp;
CL_CalcViewValues();
VectorScale( cl.v_right, stereo_separation, tmp );
VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
cl.refdef.vieworg[0] += 1.0/16;
cl.refdef.vieworg[1] += 1.0/16;
cl.refdef.vieworg[2] += 1.0/16;
cl.refdef.time = cl.time*0.001;
}
cl.refdef.x = scr_vrect.x;
cl.refdef.y = scr_vrect.y;
cl.refdef.width = scr_vrect.width;
cl.refdef.height = scr_vrect.height;
cl.refdef.fov_y = CalcFov(cl.refdef.fov_x, (float)cl.refdef.width,
(float)cl.refdef.height);
R_RenderFrame(&cl.refdef);
if (cl_stats->value)
{
Com_Printf("ent:%i lt:%i part:%i\n", r_numentities,
r_numdlights, r_numparticles);
}
if (log_stats->value && (log_stats_file != 0))
{
fprintf(log_stats_file, "%i,%i,%i,", r_numentities,
r_numdlights, r_numparticles);
}
SCR_AddDirtyPoint(scr_vrect.x, scr_vrect.y);
SCR_AddDirtyPoint(scr_vrect.x + scr_vrect.width - 1,
scr_vrect.y + scr_vrect.height - 1);
SCR_DrawCrosshair();
}
void
V_Render3dCrosshair(void)
{
trace_t crosshair_trace;
vec3_t end;
crosshair_3d = Cvar_Get("crosshair_3d", "0", CVAR_ARCHIVE);
crosshair_3d_glow = Cvar_Get("crosshair_3d_glow", "0", CVAR_ARCHIVE);
if(crosshair_3d->value || crosshair_3d_glow->value){
VectorMA(cl.refdef.vieworg,8192,cl.v_forward,end);
crosshair_trace = CL_PMTrace(cl.refdef.vieworg, vec3_origin, vec3_origin, end);
if(crosshair_3d_glow->value){
crosshair_3d_glow_r = Cvar_Get("crosshair_3d_glow_r", "5", CVAR_ARCHIVE);
crosshair_3d_glow_g = Cvar_Get("crosshair_3d_glow_g", "1", CVAR_ARCHIVE);
crosshair_3d_glow_b = Cvar_Get("crosshair_3d_glow_b", "4", CVAR_ARCHIVE);
V_AddLight(
crosshair_trace.endpos,
crosshair_3d_glow->value,
crosshair_3d_glow_r->value,
crosshair_3d_glow_g->value,
crosshair_3d_glow_b->value
);
}
if(crosshair_3d->value){
entity_t crosshair_ent = {0};
crosshair_ent.origin[0] = crosshair_trace.endpos[0];
crosshair_ent.origin[1] = crosshair_trace.endpos[1];
crosshair_ent.origin[2] = crosshair_trace.endpos[2];
crosshair_ent.model = R_RegisterModel("models/crosshair/tris.md2");
//crosshair_ent.skin = R_RegisterSkin("models/crosshair/skin.pcx");
AngleVectors2(crosshair_trace.plane.normal, crosshair_ent.angles);
crosshair_ent.flags = RF_DEPTHHACK | RF_FULLBRIGHT | RF_NOSHADOW;
V_AddEntity(&crosshair_ent);
}
}
}
void
V_Viewpos_f(void)
{
Com_Printf("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
(int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
(int)cl.refdef.viewangles[YAW]);
}
void
V_Init(void)
{
Cmd_AddCommand("gun_next", V_Gun_Next_f);
Cmd_AddCommand("gun_prev", V_Gun_Prev_f);
Cmd_AddCommand("gun_model", V_Gun_Model_f);
Cmd_AddCommand("viewpos", V_Viewpos_f);
crosshair = Cvar_Get("crosshair", "0", CVAR_ARCHIVE);
crosshair_scale = Cvar_Get("crosshair_scale", "-1", CVAR_ARCHIVE);
cl_testblend = Cvar_Get("cl_testblend", "0", 0);
cl_testparticles = Cvar_Get("cl_testparticles", "0", 0);
cl_testentities = Cvar_Get("cl_testentities", "0", 0);
cl_testlights = Cvar_Get("cl_testlights", "0", 0);
cl_stats = Cvar_Get("cl_stats", "0", 0);
}