You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1422 lines
29 KiB
1422 lines
29 KiB
/* |
|
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
|
|
|