mirror of
https://git.code.sf.net/p/quake/quake2forge
synced 2024-12-12 13:42:21 +00:00
1422 lines
29 KiB
C
1422 lines
29 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.
|
|
|
|
*/
|
|
// r_main.c
|
|
|
|
#include "r_local.h"
|
|
|
|
viddef_t vid;
|
|
refimport_t ri;
|
|
|
|
unsigned d_8to24table[256];
|
|
|
|
entity_t r_worldentity;
|
|
|
|
char skyname[MAX_QPATH];
|
|
float skyrotate;
|
|
vec3_t skyaxis;
|
|
image_t *sky_images[6];
|
|
|
|
refdef_t r_newrefdef;
|
|
model_t *currentmodel;
|
|
|
|
model_t *r_worldmodel;
|
|
|
|
byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT];
|
|
|
|
swstate_t sw_state;
|
|
|
|
void *colormap;
|
|
vec3_t viewlightvec;
|
|
alight_t r_viewlighting = {128, 192, viewlightvec};
|
|
float r_time1;
|
|
int r_numallocatededges;
|
|
float r_aliasuvscale = 1.0;
|
|
int r_outofsurfaces;
|
|
int r_outofedges;
|
|
|
|
qboolean r_dowarp;
|
|
|
|
mvertex_t *r_pcurrentvertbase;
|
|
|
|
int c_surf;
|
|
int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs;
|
|
qboolean r_surfsonstack;
|
|
int r_clipflags;
|
|
|
|
//
|
|
// view origin
|
|
//
|
|
vec3_t vup, base_vup;
|
|
vec3_t vpn, base_vpn;
|
|
vec3_t vright, base_vright;
|
|
vec3_t r_origin;
|
|
|
|
//
|
|
// screen size info
|
|
//
|
|
oldrefdef_t r_refdef;
|
|
float xcenter, ycenter;
|
|
float xscale, yscale;
|
|
float xscaleinv, yscaleinv;
|
|
float xscaleshrink, yscaleshrink;
|
|
float aliasxscale, aliasyscale, aliasxcenter, aliasycenter;
|
|
|
|
int r_screenwidth;
|
|
|
|
float verticalFieldOfView;
|
|
float xOrigin, yOrigin;
|
|
|
|
mplane_t screenedge[4];
|
|
|
|
//
|
|
// refresh flags
|
|
//
|
|
int r_framecount = 1; // so frame counts initialized to 0 don't match
|
|
int r_visframecount;
|
|
int d_spanpixcount;
|
|
int r_polycount;
|
|
int r_drawnpolycount;
|
|
int r_wholepolycount;
|
|
|
|
int *pfrustum_indexes[4];
|
|
int r_frustum_indexes[4*6];
|
|
|
|
mleaf_t *r_viewleaf;
|
|
int r_viewcluster, r_oldviewcluster;
|
|
|
|
image_t *r_notexture_mip;
|
|
|
|
float da_time1, da_time2, dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2;
|
|
float se_time1, se_time2, de_time1, de_time2;
|
|
|
|
void R_MarkLeaves (void);
|
|
|
|
cvar_t *r_lefthand;
|
|
cvar_t *sw_aliasstats;
|
|
cvar_t *sw_allow_modex;
|
|
cvar_t *sw_clearcolor;
|
|
cvar_t *sw_drawflat;
|
|
cvar_t *sw_draworder;
|
|
cvar_t *sw_maxedges;
|
|
cvar_t *sw_maxsurfs;
|
|
cvar_t *sw_mode;
|
|
cvar_t *sw_reportedgeout;
|
|
cvar_t *sw_reportsurfout;
|
|
cvar_t *sw_stipplealpha;
|
|
cvar_t *sw_surfcacheoverride;
|
|
cvar_t *sw_waterwarp;
|
|
|
|
cvar_t *r_drawworld;
|
|
cvar_t *r_drawentities;
|
|
cvar_t *r_dspeeds;
|
|
cvar_t *r_fullbright;
|
|
cvar_t *r_lerpmodels;
|
|
cvar_t *r_novis;
|
|
|
|
cvar_t *r_speeds;
|
|
cvar_t *r_lightlevel; //FIXME HACK
|
|
|
|
cvar_t *vid_fullscreen;
|
|
cvar_t *vid_gamma;
|
|
|
|
//PGM
|
|
cvar_t *sw_lockpvs;
|
|
//PGM
|
|
|
|
#define STRINGER(x) "x"
|
|
|
|
|
|
#if !id386
|
|
|
|
// r_vars.c
|
|
|
|
// all global and static refresh variables are collected in a contiguous block
|
|
// to avoid cache conflicts.
|
|
|
|
//-------------------------------------------------------
|
|
// global refresh variables
|
|
//-------------------------------------------------------
|
|
|
|
// FIXME: make into one big structure, like cl or sv
|
|
// FIXME: do separately for refresh engine and driver
|
|
|
|
|
|
// d_vars.c
|
|
|
|
// all global and static refresh variables are collected in a contiguous block
|
|
// to avoid cache conflicts.
|
|
|
|
//-------------------------------------------------------
|
|
// global refresh variables
|
|
//-------------------------------------------------------
|
|
|
|
// FIXME: make into one big structure, like cl or sv
|
|
// FIXME: do separately for refresh engine and driver
|
|
|
|
float d_sdivzstepu, d_tdivzstepu, d_zistepu;
|
|
float d_sdivzstepv, d_tdivzstepv, d_zistepv;
|
|
float d_sdivzorigin, d_tdivzorigin, d_ziorigin;
|
|
|
|
fixed16_t sadjust, tadjust, bbextents, bbextentt;
|
|
|
|
pixel_t *cacheblock;
|
|
int cachewidth;
|
|
pixel_t *d_viewbuffer;
|
|
short *d_pzbuffer;
|
|
unsigned int d_zrowbytes;
|
|
unsigned int d_zwidth;
|
|
|
|
|
|
#endif // !id386
|
|
|
|
byte r_notexture_buffer[1024];
|
|
|
|
/*
|
|
==================
|
|
R_InitTextures
|
|
==================
|
|
*/
|
|
void R_InitTextures (void)
|
|
{
|
|
int x,y, m;
|
|
byte *dest;
|
|
|
|
// create a simple checkerboard texture for the default
|
|
r_notexture_mip = (image_t *)&r_notexture_buffer;
|
|
|
|
r_notexture_mip->width = r_notexture_mip->height = 16;
|
|
r_notexture_mip->pixels[0] = &r_notexture_buffer[sizeof(image_t)];
|
|
r_notexture_mip->pixels[1] = r_notexture_mip->pixels[0] + 16*16;
|
|
r_notexture_mip->pixels[2] = r_notexture_mip->pixels[1] + 8*8;
|
|
r_notexture_mip->pixels[3] = r_notexture_mip->pixels[2] + 4*4;
|
|
|
|
for (m=0 ; m<4 ; m++)
|
|
{
|
|
dest = r_notexture_mip->pixels[m];
|
|
for (y=0 ; y< (16>>m) ; y++)
|
|
for (x=0 ; x< (16>>m) ; x++)
|
|
{
|
|
if ( (y< (8>>m) ) ^ (x< (8>>m) ) )
|
|
|
|
*dest++ = 0;
|
|
else
|
|
*dest++ = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_InitTurb
|
|
================
|
|
*/
|
|
void R_InitTurb (void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0 ; i<1280 ; i++)
|
|
{
|
|
sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP;
|
|
intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20
|
|
blanktable[i] = 0; //PGM
|
|
}
|
|
}
|
|
|
|
void R_ImageList_f( void );
|
|
|
|
void R_Register (void)
|
|
{
|
|
sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0);
|
|
sw_allow_modex = ri.Cvar_Get( "sw_allow_modex", "1", CVAR_ARCHIVE );
|
|
sw_clearcolor = ri.Cvar_Get ("sw_clearcolor", "2", 0);
|
|
sw_drawflat = ri.Cvar_Get ("sw_drawflat", "0", 0);
|
|
sw_draworder = ri.Cvar_Get ("sw_draworder", "0", 0);
|
|
sw_maxedges = ri.Cvar_Get ("sw_maxedges", STRINGER(MAXSTACKSURFACES), 0);
|
|
sw_maxsurfs = ri.Cvar_Get ("sw_maxsurfs", "0", 0);
|
|
sw_mipcap = ri.Cvar_Get ("sw_mipcap", "0", 0);
|
|
sw_mipscale = ri.Cvar_Get ("sw_mipscale", "1", 0);
|
|
sw_reportedgeout = ri.Cvar_Get ("sw_reportedgeout", "0", 0);
|
|
sw_reportsurfout = ri.Cvar_Get ("sw_reportsurfout", "0", 0);
|
|
sw_stipplealpha = ri.Cvar_Get( "sw_stipplealpha", "0", CVAR_ARCHIVE );
|
|
sw_surfcacheoverride = ri.Cvar_Get ("sw_surfcacheoverride", "0", 0);
|
|
sw_waterwarp = ri.Cvar_Get ("sw_waterwarp", "1", 0);
|
|
sw_mode = ri.Cvar_Get( "sw_mode", "0", CVAR_ARCHIVE );
|
|
|
|
r_lefthand = ri.Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE );
|
|
r_speeds = ri.Cvar_Get ("r_speeds", "0", 0);
|
|
r_fullbright = ri.Cvar_Get ("r_fullbright", "0", 0);
|
|
r_drawentities = ri.Cvar_Get ("r_drawentities", "1", 0);
|
|
r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0);
|
|
r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0);
|
|
r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0);
|
|
r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 );
|
|
r_novis = ri.Cvar_Get( "r_novis", "0", 0 );
|
|
|
|
vid_fullscreen = ri.Cvar_Get( "vid_fullscreen", "0", CVAR_ARCHIVE );
|
|
vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE );
|
|
|
|
ri.Cmd_AddCommand ("modellist", Mod_Modellist_f);
|
|
ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f );
|
|
ri.Cmd_AddCommand( "imagelist", R_ImageList_f );
|
|
|
|
sw_mode->modified = true; // force us to do mode specific stuff later
|
|
vid_gamma->modified = true; // force us to rebuild the gamma table later
|
|
|
|
//PGM
|
|
sw_lockpvs = ri.Cvar_Get ("sw_lockpvs", "0", 0);
|
|
//PGM
|
|
}
|
|
|
|
void R_UnRegister (void)
|
|
{
|
|
ri.Cmd_RemoveCommand( "screenshot" );
|
|
ri.Cmd_RemoveCommand ("modellist");
|
|
ri.Cmd_RemoveCommand( "imagelist" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_Init
|
|
===============
|
|
*/
|
|
qboolean R_Init( void *hInstance, void *wndProc )
|
|
{
|
|
R_InitImages ();
|
|
Mod_Init ();
|
|
Draw_InitLocal ();
|
|
R_InitTextures ();
|
|
|
|
R_InitTurb ();
|
|
|
|
view_clipplanes[0].leftedge = true;
|
|
view_clipplanes[1].rightedge = true;
|
|
view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =
|
|
view_clipplanes[3].leftedge = false;
|
|
view_clipplanes[0].rightedge = view_clipplanes[2].rightedge =
|
|
view_clipplanes[3].rightedge = false;
|
|
|
|
r_refdef.xOrigin = XCENTERING;
|
|
r_refdef.yOrigin = YCENTERING;
|
|
|
|
// TODO: collect 386-specific code in one place
|
|
#if id386
|
|
Sys_MakeCodeWriteable ((long)R_EdgeCodeStart,
|
|
(long)R_EdgeCodeEnd - (long)R_EdgeCodeStart);
|
|
Sys_SetFPCW (); // get bit masks for FPCW (FIXME: is this id386?)
|
|
#endif // id386
|
|
|
|
r_aliasuvscale = 1.0;
|
|
|
|
R_Register ();
|
|
Draw_GetPalette ();
|
|
SWimp_Init( hInstance, wndProc );
|
|
|
|
// create the window
|
|
R_BeginFrame( 0 );
|
|
|
|
ri.Con_Printf (PRINT_ALL, "ref_soft version: "REF_VERSION"\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_Shutdown
|
|
===============
|
|
*/
|
|
void R_Shutdown (void)
|
|
{
|
|
// free z buffer
|
|
if (d_pzbuffer)
|
|
{
|
|
free (d_pzbuffer);
|
|
d_pzbuffer = NULL;
|
|
}
|
|
// free surface cache
|
|
if (sc_base)
|
|
{
|
|
D_FlushCaches ();
|
|
free (sc_base);
|
|
sc_base = NULL;
|
|
}
|
|
|
|
// free colormap
|
|
if (vid.colormap)
|
|
{
|
|
free (vid.colormap);
|
|
vid.colormap = NULL;
|
|
}
|
|
R_UnRegister ();
|
|
Mod_FreeAll ();
|
|
R_ShutdownImages ();
|
|
|
|
SWimp_Shutdown();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_NewMap
|
|
===============
|
|
*/
|
|
void R_NewMap (void)
|
|
{
|
|
r_viewcluster = -1;
|
|
|
|
r_cnumsurfs = sw_maxsurfs->value;
|
|
|
|
if (r_cnumsurfs <= MINSURFACES)
|
|
r_cnumsurfs = MINSURFACES;
|
|
|
|
if (r_cnumsurfs > NUMSTACKSURFACES)
|
|
{
|
|
surfaces = malloc (r_cnumsurfs * sizeof(surf_t));
|
|
surface_p = surfaces;
|
|
surf_max = &surfaces[r_cnumsurfs];
|
|
r_surfsonstack = false;
|
|
// surface 0 doesn't really exist; it's just a dummy because index 0
|
|
// is used to indicate no edge attached to surface
|
|
surfaces--;
|
|
R_SurfacePatch ();
|
|
}
|
|
else
|
|
{
|
|
r_surfsonstack = true;
|
|
}
|
|
|
|
r_maxedgesseen = 0;
|
|
r_maxsurfsseen = 0;
|
|
|
|
r_numallocatededges = sw_maxedges->value;
|
|
|
|
if (r_numallocatededges < MINEDGES)
|
|
r_numallocatededges = MINEDGES;
|
|
|
|
if (r_numallocatededges <= NUMSTACKEDGES)
|
|
{
|
|
auxedges = NULL;
|
|
}
|
|
else
|
|
{
|
|
auxedges = malloc (r_numallocatededges * sizeof(edge_t));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_MarkLeaves
|
|
|
|
Mark the leaves and nodes that are in the PVS for the current
|
|
cluster
|
|
===============
|
|
*/
|
|
void R_MarkLeaves (void)
|
|
{
|
|
byte *vis;
|
|
mnode_t *node;
|
|
int i;
|
|
mleaf_t *leaf;
|
|
int cluster;
|
|
|
|
if (r_oldviewcluster == r_viewcluster && !r_novis->value && r_viewcluster != -1)
|
|
return;
|
|
|
|
// development aid to let you run around and see exactly where
|
|
// the pvs ends
|
|
if (sw_lockpvs->value)
|
|
return;
|
|
|
|
r_visframecount++;
|
|
r_oldviewcluster = r_viewcluster;
|
|
|
|
if (r_novis->value || r_viewcluster == -1 || !r_worldmodel->vis)
|
|
{
|
|
// mark everything
|
|
for (i=0 ; i<r_worldmodel->numleafs ; i++)
|
|
r_worldmodel->leafs[i].visframe = r_visframecount;
|
|
for (i=0 ; i<r_worldmodel->numnodes ; i++)
|
|
r_worldmodel->nodes[i].visframe = r_visframecount;
|
|
return;
|
|
}
|
|
|
|
vis = Mod_ClusterPVS (r_viewcluster, r_worldmodel);
|
|
|
|
for (i=0,leaf=r_worldmodel->leafs ; i<r_worldmodel->numleafs ; i++, leaf++)
|
|
{
|
|
cluster = leaf->cluster;
|
|
if (cluster == -1)
|
|
continue;
|
|
if (vis[cluster>>3] & (1<<(cluster&7)))
|
|
{
|
|
node = (mnode_t *)leaf;
|
|
do
|
|
{
|
|
if (node->visframe == r_visframecount)
|
|
break;
|
|
node->visframe = r_visframecount;
|
|
node = node->parent;
|
|
} while (node);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
for (i=0 ; i<r_worldmodel->vis->numclusters ; i++)
|
|
{
|
|
if (vis[i>>3] & (1<<(i&7)))
|
|
{
|
|
node = (mnode_t *)&r_worldmodel->leafs[i]; // FIXME: cluster
|
|
do
|
|
{
|
|
if (node->visframe == r_visframecount)
|
|
break;
|
|
node->visframe = r_visframecount;
|
|
node = node->parent;
|
|
} while (node);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** R_DrawNullModel
|
|
**
|
|
** IMPLEMENT THIS!
|
|
*/
|
|
void R_DrawNullModel( void )
|
|
{
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawEntitiesOnList
|
|
=============
|
|
*/
|
|
void R_DrawEntitiesOnList (void)
|
|
{
|
|
int i;
|
|
qboolean translucent_entities = false;
|
|
|
|
if (!r_drawentities->value)
|
|
return;
|
|
|
|
// all bmodels have already been drawn by the edge list
|
|
for (i=0 ; i<r_newrefdef.num_entities ; i++)
|
|
{
|
|
currententity = &r_newrefdef.entities[i];
|
|
|
|
if ( currententity->flags & RF_TRANSLUCENT )
|
|
{
|
|
translucent_entities = true;
|
|
continue;
|
|
}
|
|
|
|
if ( currententity->flags & RF_BEAM )
|
|
{
|
|
modelorg[0] = -r_origin[0];
|
|
modelorg[1] = -r_origin[1];
|
|
modelorg[2] = -r_origin[2];
|
|
VectorCopy( vec3_origin, r_entorigin );
|
|
R_DrawBeam( currententity );
|
|
}
|
|
else
|
|
{
|
|
currentmodel = currententity->model;
|
|
if (!currentmodel)
|
|
{
|
|
R_DrawNullModel();
|
|
continue;
|
|
}
|
|
VectorCopy (currententity->origin, r_entorigin);
|
|
VectorSubtract (r_origin, r_entorigin, modelorg);
|
|
|
|
switch (currentmodel->type)
|
|
{
|
|
case mod_sprite:
|
|
R_DrawSprite ();
|
|
break;
|
|
|
|
case mod_alias:
|
|
R_AliasDrawModel ();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !translucent_entities )
|
|
return;
|
|
|
|
for (i=0 ; i<r_newrefdef.num_entities ; i++)
|
|
{
|
|
currententity = &r_newrefdef.entities[i];
|
|
|
|
if ( !( currententity->flags & RF_TRANSLUCENT ) )
|
|
continue;
|
|
|
|
if ( currententity->flags & RF_BEAM )
|
|
{
|
|
modelorg[0] = -r_origin[0];
|
|
modelorg[1] = -r_origin[1];
|
|
modelorg[2] = -r_origin[2];
|
|
VectorCopy( vec3_origin, r_entorigin );
|
|
R_DrawBeam( currententity );
|
|
}
|
|
else
|
|
{
|
|
currentmodel = currententity->model;
|
|
if (!currentmodel)
|
|
{
|
|
R_DrawNullModel();
|
|
continue;
|
|
}
|
|
VectorCopy (currententity->origin, r_entorigin);
|
|
VectorSubtract (r_origin, r_entorigin, modelorg);
|
|
|
|
switch (currentmodel->type)
|
|
{
|
|
case mod_sprite:
|
|
R_DrawSprite ();
|
|
break;
|
|
|
|
case mod_alias:
|
|
R_AliasDrawModel ();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
R_BmodelCheckBBox
|
|
=============
|
|
*/
|
|
int R_BmodelCheckBBox (float *minmaxs)
|
|
{
|
|
int i, *pindex, clipflags;
|
|
vec3_t acceptpt, rejectpt;
|
|
float d;
|
|
|
|
clipflags = 0;
|
|
|
|
for (i=0 ; i<4 ; i++)
|
|
{
|
|
// generate accept and reject points
|
|
// FIXME: do with fast look-ups or integer tests based on the sign bit
|
|
// of the floating point values
|
|
|
|
pindex = pfrustum_indexes[i];
|
|
|
|
rejectpt[0] = minmaxs[pindex[0]];
|
|
rejectpt[1] = minmaxs[pindex[1]];
|
|
rejectpt[2] = minmaxs[pindex[2]];
|
|
|
|
d = DotProduct (rejectpt, view_clipplanes[i].normal);
|
|
d -= view_clipplanes[i].dist;
|
|
|
|
if (d <= 0)
|
|
return BMODEL_FULLY_CLIPPED;
|
|
|
|
acceptpt[0] = minmaxs[pindex[3+0]];
|
|
acceptpt[1] = minmaxs[pindex[3+1]];
|
|
acceptpt[2] = minmaxs[pindex[3+2]];
|
|
|
|
d = DotProduct (acceptpt, view_clipplanes[i].normal);
|
|
d -= view_clipplanes[i].dist;
|
|
|
|
if (d <= 0)
|
|
clipflags |= (1<<i);
|
|
}
|
|
|
|
return clipflags;
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
R_FindTopnode
|
|
|
|
Find the first node that splits the given box
|
|
===================
|
|
*/
|
|
mnode_t *R_FindTopnode (vec3_t mins, vec3_t maxs)
|
|
{
|
|
mplane_t *splitplane;
|
|
int sides;
|
|
mnode_t *node;
|
|
|
|
node = r_worldmodel->nodes;
|
|
|
|
while (1)
|
|
{
|
|
if (node->visframe != r_visframecount)
|
|
return NULL; // not visible at all
|
|
|
|
if (node->contents != CONTENTS_NODE)
|
|
{
|
|
if (node->contents != CONTENTS_SOLID)
|
|
return node; // we've reached a non-solid leaf, so it's
|
|
// visible and not BSP clipped
|
|
return NULL; // in solid, so not visible
|
|
}
|
|
|
|
splitplane = node->plane;
|
|
sides = BOX_ON_PLANE_SIDE(mins, maxs, (cplane_t *)splitplane);
|
|
|
|
if (sides == 3)
|
|
return node; // this is the splitter
|
|
|
|
// not split yet; recurse down the contacted side
|
|
if (sides & 1)
|
|
node = node->children[0];
|
|
else
|
|
node = node->children[1];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
RotatedBBox
|
|
|
|
Returns an axially aligned box that contains the input box at the given rotation
|
|
=============
|
|
*/
|
|
void RotatedBBox (vec3_t mins, vec3_t maxs, vec3_t angles, vec3_t tmins, vec3_t tmaxs)
|
|
{
|
|
vec3_t tmp, v;
|
|
int i, j;
|
|
vec3_t forward, right, up;
|
|
|
|
if (!angles[0] && !angles[1] && !angles[2])
|
|
{
|
|
VectorCopy (mins, tmins);
|
|
VectorCopy (maxs, tmaxs);
|
|
return;
|
|
}
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
tmins[i] = 99999;
|
|
tmaxs[i] = -99999;
|
|
}
|
|
|
|
AngleVectors (angles, forward, right, up);
|
|
|
|
for ( i = 0; i < 8; i++ )
|
|
{
|
|
if ( i & 1 )
|
|
tmp[0] = mins[0];
|
|
else
|
|
tmp[0] = maxs[0];
|
|
|
|
if ( i & 2 )
|
|
tmp[1] = mins[1];
|
|
else
|
|
tmp[1] = maxs[1];
|
|
|
|
if ( i & 4 )
|
|
tmp[2] = mins[2];
|
|
else
|
|
tmp[2] = maxs[2];
|
|
|
|
|
|
VectorScale (forward, tmp[0], v);
|
|
VectorMA (v, -tmp[1], right, v);
|
|
VectorMA (v, tmp[2], up, v);
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
if (v[j] < tmins[j])
|
|
tmins[j] = v[j];
|
|
if (v[j] > tmaxs[j])
|
|
tmaxs[j] = v[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawBEntitiesOnList
|
|
=============
|
|
*/
|
|
void R_DrawBEntitiesOnList (void)
|
|
{
|
|
int i, clipflags;
|
|
vec3_t oldorigin;
|
|
vec3_t mins, maxs;
|
|
float minmaxs[6];
|
|
mnode_t *topnode;
|
|
|
|
if (!r_drawentities->value)
|
|
return;
|
|
|
|
VectorCopy (modelorg, oldorigin);
|
|
insubmodel = true;
|
|
r_dlightframecount = r_framecount;
|
|
|
|
for (i=0 ; i<r_newrefdef.num_entities ; i++)
|
|
{
|
|
currententity = &r_newrefdef.entities[i];
|
|
currentmodel = currententity->model;
|
|
if (!currentmodel)
|
|
continue;
|
|
if (currentmodel->nummodelsurfaces == 0)
|
|
continue; // clip brush only
|
|
if ( currententity->flags & RF_BEAM )
|
|
continue;
|
|
if (currentmodel->type != mod_brush)
|
|
continue;
|
|
// see if the bounding box lets us trivially reject, also sets
|
|
// trivial accept status
|
|
RotatedBBox (currentmodel->mins, currentmodel->maxs,
|
|
currententity->angles, mins, maxs);
|
|
VectorAdd (mins, currententity->origin, minmaxs);
|
|
VectorAdd (maxs, currententity->origin, (minmaxs+3));
|
|
|
|
clipflags = R_BmodelCheckBBox (minmaxs);
|
|
if (clipflags == BMODEL_FULLY_CLIPPED)
|
|
continue; // off the edge of the screen
|
|
|
|
topnode = R_FindTopnode (minmaxs, minmaxs+3);
|
|
if (!topnode)
|
|
continue; // no part in a visible leaf
|
|
|
|
VectorCopy (currententity->origin, r_entorigin);
|
|
VectorSubtract (r_origin, r_entorigin, modelorg);
|
|
|
|
r_pcurrentvertbase = currentmodel->vertexes;
|
|
|
|
// FIXME: stop transforming twice
|
|
R_RotateBmodel ();
|
|
|
|
// calculate dynamic lighting for bmodel
|
|
R_PushDlights (currentmodel);
|
|
|
|
if (topnode->contents == CONTENTS_NODE)
|
|
{
|
|
// not a leaf; has to be clipped to the world BSP
|
|
r_clipflags = clipflags;
|
|
R_DrawSolidClippedSubmodelPolygons (currentmodel, topnode);
|
|
}
|
|
else
|
|
{
|
|
// falls entirely in one leaf, so we just put all the
|
|
// edges in the edge list and let 1/z sorting handle
|
|
// drawing order
|
|
R_DrawSubmodelPolygons (currentmodel, clipflags, topnode);
|
|
}
|
|
|
|
// put back world rotation and frustum clipping
|
|
// FIXME: R_RotateBmodel should just work off base_vxx
|
|
VectorCopy (base_vpn, vpn);
|
|
VectorCopy (base_vup, vup);
|
|
VectorCopy (base_vright, vright);
|
|
VectorCopy (oldorigin, modelorg);
|
|
R_TransformFrustum ();
|
|
}
|
|
|
|
insubmodel = false;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_EdgeDrawing
|
|
================
|
|
*/
|
|
void R_EdgeDrawing (void)
|
|
{
|
|
edge_t ledges[NUMSTACKEDGES +
|
|
((CACHE_SIZE - 1) / sizeof(edge_t)) + 1];
|
|
surf_t lsurfs[NUMSTACKSURFACES +
|
|
((CACHE_SIZE - 1) / sizeof(surf_t)) + 1];
|
|
|
|
if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL )
|
|
return;
|
|
|
|
if (auxedges)
|
|
{
|
|
r_edges = auxedges;
|
|
}
|
|
else
|
|
{
|
|
r_edges = (edge_t *)
|
|
(((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
|
|
}
|
|
|
|
if (r_surfsonstack)
|
|
{
|
|
surfaces = (surf_t *)
|
|
(((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
|
|
surf_max = &surfaces[r_cnumsurfs];
|
|
// surface 0 doesn't really exist; it's just a dummy because index 0
|
|
// is used to indicate no edge attached to surface
|
|
surfaces--;
|
|
R_SurfacePatch ();
|
|
}
|
|
|
|
R_BeginEdgeFrame ();
|
|
|
|
if (r_dspeeds->value)
|
|
{
|
|
rw_time1 = Sys_Milliseconds ();
|
|
}
|
|
|
|
R_RenderWorld ();
|
|
|
|
if (r_dspeeds->value)
|
|
{
|
|
rw_time2 = Sys_Milliseconds ();
|
|
db_time1 = rw_time2;
|
|
}
|
|
|
|
R_DrawBEntitiesOnList ();
|
|
|
|
if (r_dspeeds->value)
|
|
{
|
|
db_time2 = Sys_Milliseconds ();
|
|
se_time1 = db_time2;
|
|
}
|
|
|
|
R_ScanEdges ();
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
|
|
/*
|
|
=============
|
|
R_CalcPalette
|
|
|
|
=============
|
|
*/
|
|
void R_CalcPalette (void)
|
|
{
|
|
static qboolean modified;
|
|
byte palette[256][4], *in, *out;
|
|
int i, j;
|
|
float alpha, one_minus_alpha;
|
|
vec3_t premult;
|
|
int v;
|
|
|
|
alpha = r_newrefdef.blend[3];
|
|
if (alpha <= 0)
|
|
{
|
|
if (modified)
|
|
{ // set back to default
|
|
modified = false;
|
|
R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table );
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
modified = true;
|
|
if (alpha > 1)
|
|
alpha = 1;
|
|
|
|
premult[0] = r_newrefdef.blend[0]*alpha*255;
|
|
premult[1] = r_newrefdef.blend[1]*alpha*255;
|
|
premult[2] = r_newrefdef.blend[2]*alpha*255;
|
|
|
|
one_minus_alpha = (1.0 - alpha);
|
|
|
|
in = (byte *)d_8to24table;
|
|
out = palette[0];
|
|
for (i=0 ; i<256 ; i++, in+=4, out+=4)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
v = premult[j] + one_minus_alpha * in[j];
|
|
if (v > 255)
|
|
v = 255;
|
|
out[j] = v;
|
|
}
|
|
out[3] = 255;
|
|
}
|
|
|
|
R_GammaCorrectAndSetPalette( ( const unsigned char * ) palette[0] );
|
|
// SWimp_SetPalette( palette[0] );
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
void R_SetLightLevel (void)
|
|
{
|
|
vec3_t light;
|
|
|
|
if ((r_newrefdef.rdflags & RDF_NOWORLDMODEL) || (!r_drawentities->value) || (!currententity))
|
|
{
|
|
r_lightlevel->value = 150.0;
|
|
return;
|
|
}
|
|
|
|
// save off light value for server to look at (BIG HACK!)
|
|
R_LightPoint (r_newrefdef.vieworg, light);
|
|
r_lightlevel->value = 150.0 * light[0];
|
|
}
|
|
|
|
|
|
/*
|
|
@@@@@@@@@@@@@@@@
|
|
R_RenderFrame
|
|
|
|
@@@@@@@@@@@@@@@@
|
|
*/
|
|
void R_RenderFrame (refdef_t *fd)
|
|
{
|
|
r_newrefdef = *fd;
|
|
|
|
if (!r_worldmodel && !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) )
|
|
ri.Sys_Error (ERR_FATAL,"R_RenderView: NULL worldmodel");
|
|
|
|
VectorCopy (fd->vieworg, r_refdef.vieworg);
|
|
VectorCopy (fd->viewangles, r_refdef.viewangles);
|
|
|
|
if (r_speeds->value || r_dspeeds->value)
|
|
r_time1 = Sys_Milliseconds ();
|
|
|
|
R_SetupFrame ();
|
|
|
|
R_MarkLeaves (); // done here so we know if we're in water
|
|
|
|
R_PushDlights (r_worldmodel);
|
|
|
|
R_EdgeDrawing ();
|
|
|
|
if (r_dspeeds->value)
|
|
{
|
|
se_time2 = Sys_Milliseconds ();
|
|
de_time1 = se_time2;
|
|
}
|
|
|
|
R_DrawEntitiesOnList ();
|
|
|
|
if (r_dspeeds->value)
|
|
{
|
|
de_time2 = Sys_Milliseconds ();
|
|
dp_time1 = Sys_Milliseconds ();
|
|
}
|
|
|
|
R_DrawParticles ();
|
|
|
|
if (r_dspeeds->value)
|
|
dp_time2 = Sys_Milliseconds ();
|
|
|
|
R_DrawAlphaSurfaces();
|
|
|
|
R_SetLightLevel ();
|
|
|
|
if (r_dowarp)
|
|
D_WarpScreen ();
|
|
|
|
if (r_dspeeds->value)
|
|
da_time1 = Sys_Milliseconds ();
|
|
|
|
if (r_dspeeds->value)
|
|
da_time2 = Sys_Milliseconds ();
|
|
|
|
R_CalcPalette ();
|
|
|
|
if (sw_aliasstats->value)
|
|
R_PrintAliasStats ();
|
|
|
|
if (r_speeds->value)
|
|
R_PrintTimes ();
|
|
|
|
if (r_dspeeds->value)
|
|
R_PrintDSpeeds ();
|
|
|
|
if (sw_reportsurfout->value && r_outofsurfaces)
|
|
ri.Con_Printf (PRINT_ALL,"Short %d surfaces\n", r_outofsurfaces);
|
|
|
|
if (sw_reportedgeout->value && r_outofedges)
|
|
ri.Con_Printf (PRINT_ALL,"Short roughly %d edges\n", r_outofedges * 2 / 3);
|
|
}
|
|
|
|
/*
|
|
** R_InitGraphics
|
|
*/
|
|
void R_InitGraphics( int width, int height )
|
|
{
|
|
vid.width = width;
|
|
vid.height = height;
|
|
|
|
// free z buffer
|
|
if ( d_pzbuffer )
|
|
{
|
|
free( d_pzbuffer );
|
|
d_pzbuffer = NULL;
|
|
}
|
|
|
|
// free surface cache
|
|
if ( sc_base )
|
|
{
|
|
D_FlushCaches ();
|
|
free( sc_base );
|
|
sc_base = NULL;
|
|
}
|
|
|
|
d_pzbuffer = malloc(vid.width*vid.height*2);
|
|
|
|
R_InitCaches ();
|
|
|
|
R_GammaCorrectAndSetPalette( ( const unsigned char *) d_8to24table );
|
|
}
|
|
|
|
/*
|
|
** R_BeginFrame
|
|
*/
|
|
void R_BeginFrame( float camera_separation )
|
|
{
|
|
extern void Draw_BuildGammaTable( void );
|
|
|
|
/*
|
|
** rebuild the gamma correction palette if necessary
|
|
*/
|
|
if ( vid_gamma->modified )
|
|
{
|
|
Draw_BuildGammaTable();
|
|
R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table );
|
|
|
|
vid_gamma->modified = false;
|
|
}
|
|
|
|
while ( sw_mode->modified || vid_fullscreen->modified )
|
|
{
|
|
rserr_t err;
|
|
|
|
/*
|
|
** if this returns rserr_invalid_fullscreen then it set the mode but not as a
|
|
** fullscreen mode, e.g. 320x200 on a system that doesn't support that res
|
|
*/
|
|
if ( ( err = SWimp_SetMode( &vid.width, &vid.height, sw_mode->value, vid_fullscreen->value ) ) == rserr_ok )
|
|
{
|
|
R_InitGraphics( vid.width, vid.height );
|
|
|
|
sw_state.prev_mode = sw_mode->value;
|
|
vid_fullscreen->modified = false;
|
|
sw_mode->modified = false;
|
|
}
|
|
else
|
|
{
|
|
if ( err == rserr_invalid_mode )
|
|
{
|
|
ri.Cvar_SetValue( "sw_mode", sw_state.prev_mode );
|
|
ri.Con_Printf( PRINT_ALL, "ref_soft::R_BeginFrame() - could not set mode\n" );
|
|
}
|
|
else if ( err == rserr_invalid_fullscreen )
|
|
{
|
|
R_InitGraphics( vid.width, vid.height );
|
|
|
|
ri.Cvar_SetValue( "vid_fullscreen", 0);
|
|
ri.Con_Printf( PRINT_ALL, "ref_soft::R_BeginFrame() - fullscreen unavailable in this mode\n" );
|
|
sw_state.prev_mode = sw_mode->value;
|
|
// vid_fullscreen->modified = false;
|
|
// sw_mode->modified = false;
|
|
}
|
|
else
|
|
{
|
|
ri.Sys_Error( ERR_FATAL, "ref_soft::R_BeginFrame() - catastrophic mode change failure\n" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** R_GammaCorrectAndSetPalette
|
|
*/
|
|
void R_GammaCorrectAndSetPalette( const unsigned char *palette )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < 256; i++ )
|
|
{
|
|
sw_state.currentpalette[i*4+0] = sw_state.gammatable[palette[i*4+0]];
|
|
sw_state.currentpalette[i*4+1] = sw_state.gammatable[palette[i*4+1]];
|
|
sw_state.currentpalette[i*4+2] = sw_state.gammatable[palette[i*4+2]];
|
|
}
|
|
|
|
SWimp_SetPalette( sw_state.currentpalette );
|
|
}
|
|
|
|
/*
|
|
** R_CinematicSetPalette
|
|
*/
|
|
void R_CinematicSetPalette( const unsigned char *palette )
|
|
{
|
|
byte palette32[1024];
|
|
int i, j, w;
|
|
int *d;
|
|
|
|
// clear screen to black to avoid any palette flash
|
|
w = abs(vid.rowbytes)>>2; // stupid negative pitch win32 stuff...
|
|
for (i=0 ; i<vid.height ; i++, d+=w)
|
|
{
|
|
d = (int *)(vid.buffer + i*vid.rowbytes);
|
|
for (j=0 ; j<w ; j++)
|
|
d[j] = 0;
|
|
}
|
|
// flush it to the screen
|
|
SWimp_EndFrame ();
|
|
|
|
if ( palette )
|
|
{
|
|
for ( i = 0; i < 256; i++ )
|
|
{
|
|
palette32[i*4+0] = palette[i*3+0];
|
|
palette32[i*4+1] = palette[i*3+1];
|
|
palette32[i*4+2] = palette[i*3+2];
|
|
palette32[i*4+3] = 0xFF;
|
|
}
|
|
|
|
R_GammaCorrectAndSetPalette( palette32 );
|
|
}
|
|
else
|
|
{
|
|
R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Draw_BuildGammaTable
|
|
================
|
|
*/
|
|
void Draw_BuildGammaTable (void)
|
|
{
|
|
int i, inf;
|
|
float g;
|
|
|
|
g = vid_gamma->value;
|
|
|
|
if (g == 1.0)
|
|
{
|
|
for (i=0 ; i<256 ; i++)
|
|
sw_state.gammatable[i] = i;
|
|
return;
|
|
}
|
|
|
|
for (i=0 ; i<256 ; i++)
|
|
{
|
|
inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
|
|
if (inf < 0)
|
|
inf = 0;
|
|
if (inf > 255)
|
|
inf = 255;
|
|
sw_state.gammatable[i] = inf;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** R_DrawBeam
|
|
*/
|
|
void R_DrawBeam( entity_t *e )
|
|
{
|
|
#define NUM_BEAM_SEGS 6
|
|
|
|
int i;
|
|
|
|
vec3_t perpvec;
|
|
vec3_t direction, normalized_direction;
|
|
vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS];
|
|
vec3_t oldorigin, origin;
|
|
|
|
oldorigin[0] = e->oldorigin[0];
|
|
oldorigin[1] = e->oldorigin[1];
|
|
oldorigin[2] = e->oldorigin[2];
|
|
|
|
origin[0] = e->origin[0];
|
|
origin[1] = e->origin[1];
|
|
origin[2] = e->origin[2];
|
|
|
|
normalized_direction[0] = direction[0] = oldorigin[0] - origin[0];
|
|
normalized_direction[1] = direction[1] = oldorigin[1] - origin[1];
|
|
normalized_direction[2] = direction[2] = oldorigin[2] - origin[2];
|
|
|
|
if ( VectorNormalize( normalized_direction ) == 0 )
|
|
return;
|
|
|
|
PerpendicularVector( perpvec, normalized_direction );
|
|
VectorScale( perpvec, e->frame / 2, perpvec );
|
|
|
|
for ( i = 0; i < NUM_BEAM_SEGS; i++ )
|
|
{
|
|
RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i );
|
|
VectorAdd( start_points[i], origin, start_points[i] );
|
|
VectorAdd( start_points[i], direction, end_points[i] );
|
|
}
|
|
|
|
for ( i = 0; i < NUM_BEAM_SEGS; i++ )
|
|
{
|
|
R_IMFlatShadedQuad( start_points[i],
|
|
end_points[i],
|
|
end_points[(i+1)%NUM_BEAM_SEGS],
|
|
start_points[(i+1)%NUM_BEAM_SEGS],
|
|
e->skinnum & 0xFF,
|
|
e->alpha );
|
|
}
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
|
|
/*
|
|
============
|
|
R_SetSky
|
|
============
|
|
*/
|
|
// 3dstudio environment map names
|
|
char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
|
|
int r_skysideimage[6] = {5, 2, 4, 1, 0, 3};
|
|
extern mtexinfo_t r_skytexinfo[6];
|
|
void R_SetSky (char *name, float rotate, vec3_t axis)
|
|
{
|
|
int i;
|
|
char pathname[MAX_QPATH];
|
|
|
|
strncpy (skyname, name, sizeof(skyname)-1);
|
|
skyrotate = rotate;
|
|
VectorCopy (axis, skyaxis);
|
|
|
|
for (i=0 ; i<6 ; i++)
|
|
{
|
|
Com_sprintf (pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[r_skysideimage[i]]);
|
|
r_skytexinfo[i].image = R_FindImage (pathname, it_sky);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Draw_GetPalette
|
|
===============
|
|
*/
|
|
void Draw_GetPalette (void)
|
|
{
|
|
byte *pal, *out;
|
|
int i;
|
|
int r, g, b;
|
|
|
|
// get the palette and colormap
|
|
LoadPCX ("pics/colormap.pcx", &vid.colormap, &pal, NULL, NULL);
|
|
if (!vid.colormap)
|
|
ri.Sys_Error (ERR_FATAL, "Couldn't load pics/colormap.pcx");
|
|
vid.alphamap = vid.colormap + 64*256;
|
|
|
|
out = (byte *)d_8to24table;
|
|
for (i=0 ; i<256 ; i++, out+=4)
|
|
{
|
|
r = pal[i*3+0];
|
|
g = pal[i*3+1];
|
|
b = pal[i*3+2];
|
|
|
|
out[0] = r;
|
|
out[1] = g;
|
|
out[2] = b;
|
|
}
|
|
|
|
free (pal);
|
|
}
|
|
|
|
struct image_s *R_RegisterSkin (char *name);
|
|
|
|
/*
|
|
@@@@@@@@@@@@@@@@@@@@@
|
|
GetRefAPI
|
|
|
|
@@@@@@@@@@@@@@@@@@@@@
|
|
*/
|
|
refexport_t GetRefAPI (refimport_t rimp)
|
|
{
|
|
refexport_t re;
|
|
|
|
ri = rimp;
|
|
|
|
re.api_version = API_VERSION;
|
|
|
|
re.BeginRegistration = R_BeginRegistration;
|
|
re.RegisterModel = R_RegisterModel;
|
|
re.RegisterSkin = R_RegisterSkin;
|
|
re.RegisterPic = Draw_FindPic;
|
|
re.SetSky = R_SetSky;
|
|
re.EndRegistration = R_EndRegistration;
|
|
|
|
re.RenderFrame = R_RenderFrame;
|
|
|
|
re.DrawGetPicSize = Draw_GetPicSize;
|
|
re.DrawPic = Draw_Pic;
|
|
re.DrawStretchPic = Draw_StretchPic;
|
|
re.DrawChar = Draw_Char;
|
|
re.DrawTileClear = Draw_TileClear;
|
|
re.DrawFill = Draw_Fill;
|
|
re.DrawFadeScreen= Draw_FadeScreen;
|
|
|
|
re.DrawStretchRaw = Draw_StretchRaw;
|
|
|
|
re.Init = R_Init;
|
|
re.Shutdown = R_Shutdown;
|
|
|
|
re.CinematicSetPalette = R_CinematicSetPalette;
|
|
re.BeginFrame = R_BeginFrame;
|
|
re.EndFrame = SWimp_EndFrame;
|
|
|
|
re.AppActivate = SWimp_AppActivate;
|
|
|
|
Swap_Init ();
|
|
|
|
return re;
|
|
}
|
|
|
|
#ifndef REF_HARD_LINKED
|
|
// this is only here so the functions in q_shared.c and q_shwin.c can link
|
|
void Sys_Error (char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char text[1024];
|
|
|
|
va_start (argptr, error);
|
|
vsprintf (text, error, argptr);
|
|
va_end (argptr);
|
|
|
|
ri.Sys_Error (ERR_FATAL, "%s", text);
|
|
}
|
|
|
|
void Com_Printf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char text[1024];
|
|
|
|
va_start (argptr, fmt);
|
|
vsprintf (text, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
ri.Con_Printf (PRINT_ALL, "%s", text);
|
|
}
|
|
|
|
#endif
|