Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

tr_mesh.c (10192B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // tr_mesh.c: triangle model functions
     23 
     24 #include "tr_local.h"
     25 
     26 static float ProjectRadius( float r, vec3_t location )
     27 {
     28 	float pr;
     29 	float dist;
     30 	float c;
     31 	vec3_t	p;
     32 	float	projected[4];
     33 
     34 	c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin );
     35 	dist = DotProduct( tr.viewParms.or.axis[0], location ) - c;
     36 
     37 	if ( dist <= 0 )
     38 		return 0;
     39 
     40 	p[0] = 0;
     41 	p[1] = fabs( r );
     42 	p[2] = -dist;
     43 
     44 	projected[0] = p[0] * tr.viewParms.projectionMatrix[0] + 
     45 		           p[1] * tr.viewParms.projectionMatrix[4] +
     46 				   p[2] * tr.viewParms.projectionMatrix[8] +
     47 				   tr.viewParms.projectionMatrix[12];
     48 
     49 	projected[1] = p[0] * tr.viewParms.projectionMatrix[1] + 
     50 		           p[1] * tr.viewParms.projectionMatrix[5] +
     51 				   p[2] * tr.viewParms.projectionMatrix[9] +
     52 				   tr.viewParms.projectionMatrix[13];
     53 
     54 	projected[2] = p[0] * tr.viewParms.projectionMatrix[2] + 
     55 		           p[1] * tr.viewParms.projectionMatrix[6] +
     56 				   p[2] * tr.viewParms.projectionMatrix[10] +
     57 				   tr.viewParms.projectionMatrix[14];
     58 
     59 	projected[3] = p[0] * tr.viewParms.projectionMatrix[3] + 
     60 		           p[1] * tr.viewParms.projectionMatrix[7] +
     61 				   p[2] * tr.viewParms.projectionMatrix[11] +
     62 				   tr.viewParms.projectionMatrix[15];
     63 
     64 
     65 	pr = projected[1] / projected[3];
     66 
     67 	if ( pr > 1.0f )
     68 		pr = 1.0f;
     69 
     70 	return pr;
     71 }
     72 
     73 /*
     74 =============
     75 R_CullModel
     76 =============
     77 */
     78 static int R_CullModel( md3Header_t *header, trRefEntity_t *ent ) {
     79 	vec3_t		bounds[2];
     80 	md3Frame_t	*oldFrame, *newFrame;
     81 	int			i;
     82 
     83 	// compute frame pointers
     84 	newFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame;
     85 	oldFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.oldframe;
     86 
     87 	// cull bounding sphere ONLY if this is not an upscaled entity
     88 	if ( !ent->e.nonNormalizedAxes )
     89 	{
     90 		if ( ent->e.frame == ent->e.oldframe )
     91 		{
     92 			switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
     93 			{
     94 			case CULL_OUT:
     95 				tr.pc.c_sphere_cull_md3_out++;
     96 				return CULL_OUT;
     97 
     98 			case CULL_IN:
     99 				tr.pc.c_sphere_cull_md3_in++;
    100 				return CULL_IN;
    101 
    102 			case CULL_CLIP:
    103 				tr.pc.c_sphere_cull_md3_clip++;
    104 				break;
    105 			}
    106 		}
    107 		else
    108 		{
    109 			int sphereCull, sphereCullB;
    110 
    111 			sphereCull  = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
    112 			if ( newFrame == oldFrame ) {
    113 				sphereCullB = sphereCull;
    114 			} else {
    115 				sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
    116 			}
    117 
    118 			if ( sphereCull == sphereCullB )
    119 			{
    120 				if ( sphereCull == CULL_OUT )
    121 				{
    122 					tr.pc.c_sphere_cull_md3_out++;
    123 					return CULL_OUT;
    124 				}
    125 				else if ( sphereCull == CULL_IN )
    126 				{
    127 					tr.pc.c_sphere_cull_md3_in++;
    128 					return CULL_IN;
    129 				}
    130 				else
    131 				{
    132 					tr.pc.c_sphere_cull_md3_clip++;
    133 				}
    134 			}
    135 		}
    136 	}
    137 	
    138 	// calculate a bounding box in the current coordinate system
    139 	for (i = 0 ; i < 3 ; i++) {
    140 		bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
    141 		bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
    142 	}
    143 
    144 	switch ( R_CullLocalBox( bounds ) )
    145 	{
    146 	case CULL_IN:
    147 		tr.pc.c_box_cull_md3_in++;
    148 		return CULL_IN;
    149 	case CULL_CLIP:
    150 		tr.pc.c_box_cull_md3_clip++;
    151 		return CULL_CLIP;
    152 	case CULL_OUT:
    153 	default:
    154 		tr.pc.c_box_cull_md3_out++;
    155 		return CULL_OUT;
    156 	}
    157 }
    158 
    159 
    160 /*
    161 =================
    162 R_ComputeLOD
    163 
    164 =================
    165 */
    166 int R_ComputeLOD( trRefEntity_t *ent ) {
    167 	float radius;
    168 	float flod, lodscale;
    169 	float projectedRadius;
    170 	md3Frame_t *frame;
    171 	int lod;
    172 
    173 	if ( tr.currentModel->numLods < 2 )
    174 	{
    175 		// model has only 1 LOD level, skip computations and bias
    176 		lod = 0;
    177 	}
    178 	else
    179 	{
    180 		// multiple LODs exist, so compute projected bounding sphere
    181 		// and use that as a criteria for selecting LOD
    182 
    183 		frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames );
    184 
    185 		frame += ent->e.frame;
    186 
    187 		radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] );
    188 
    189 		if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 )
    190 		{
    191 			lodscale = r_lodscale->value;
    192 			if (lodscale > 20) lodscale = 20;
    193 			flod = 1.0f - projectedRadius * lodscale;
    194 		}
    195 		else
    196 		{
    197 			// object intersects near view plane, e.g. view weapon
    198 			flod = 0;
    199 		}
    200 
    201 		flod *= tr.currentModel->numLods;
    202 		lod = myftol( flod );
    203 
    204 		if ( lod < 0 )
    205 		{
    206 			lod = 0;
    207 		}
    208 		else if ( lod >= tr.currentModel->numLods )
    209 		{
    210 			lod = tr.currentModel->numLods - 1;
    211 		}
    212 	}
    213 
    214 	lod += r_lodbias->integer;
    215 	
    216 	if ( lod >= tr.currentModel->numLods )
    217 		lod = tr.currentModel->numLods - 1;
    218 	if ( lod < 0 )
    219 		lod = 0;
    220 
    221 	return lod;
    222 }
    223 
    224 /*
    225 =================
    226 R_ComputeFogNum
    227 
    228 =================
    229 */
    230 int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) {
    231 	int				i, j;
    232 	fog_t			*fog;
    233 	md3Frame_t		*md3Frame;
    234 	vec3_t			localOrigin;
    235 
    236 	if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
    237 		return 0;
    238 	}
    239 
    240 	// FIXME: non-normalized axis issues
    241 	md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame;
    242 	VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin );
    243 	for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
    244 		fog = &tr.world->fogs[i];
    245 		for ( j = 0 ; j < 3 ; j++ ) {
    246 			if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) {
    247 				break;
    248 			}
    249 			if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) {
    250 				break;
    251 			}
    252 		}
    253 		if ( j == 3 ) {
    254 			return i;
    255 		}
    256 	}
    257 
    258 	return 0;
    259 }
    260 
    261 /*
    262 =================
    263 R_AddMD3Surfaces
    264 
    265 =================
    266 */
    267 void R_AddMD3Surfaces( trRefEntity_t *ent ) {
    268 	int				i;
    269 	md3Header_t		*header = 0;
    270 	md3Surface_t	*surface = 0;
    271 	md3Shader_t		*md3Shader = 0;
    272 	shader_t		*shader = 0;
    273 	int				cull;
    274 	int				lod;
    275 	int				fogNum;
    276 	qboolean		personalModel;
    277 
    278 	// don't add third_person objects if not in a portal
    279 	personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
    280 
    281 	if ( ent->e.renderfx & RF_WRAP_FRAMES ) {
    282 		ent->e.frame %= tr.currentModel->md3[0]->numFrames;
    283 		ent->e.oldframe %= tr.currentModel->md3[0]->numFrames;
    284 	}
    285 
    286 	//
    287 	// Validate the frames so there is no chance of a crash.
    288 	// This will write directly into the entity structure, so
    289 	// when the surfaces are rendered, they don't need to be
    290 	// range checked again.
    291 	//
    292 	if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames) 
    293 		|| (ent->e.frame < 0)
    294 		|| (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames)
    295 		|| (ent->e.oldframe < 0) ) {
    296 			ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n",
    297 				ent->e.oldframe, ent->e.frame,
    298 				tr.currentModel->name );
    299 			ent->e.frame = 0;
    300 			ent->e.oldframe = 0;
    301 	}
    302 
    303 	//
    304 	// compute LOD
    305 	//
    306 	lod = R_ComputeLOD( ent );
    307 
    308 	header = tr.currentModel->md3[lod];
    309 
    310 	//
    311 	// cull the entire model if merged bounding box of both frames
    312 	// is outside the view frustum.
    313 	//
    314 	cull = R_CullModel ( header, ent );
    315 	if ( cull == CULL_OUT ) {
    316 		return;
    317 	}
    318 
    319 	//
    320 	// set up lighting now that we know we aren't culled
    321 	//
    322 	if ( !personalModel || r_shadows->integer > 1 ) {
    323 		R_SetupEntityLighting( &tr.refdef, ent );
    324 	}
    325 
    326 	//
    327 	// see if we are in a fog volume
    328 	//
    329 	fogNum = R_ComputeFogNum( header, ent );
    330 
    331 	//
    332 	// draw all surfaces
    333 	//
    334 	surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces );
    335 	for ( i = 0 ; i < header->numSurfaces ; i++ ) {
    336 
    337 		if ( ent->e.customShader ) {
    338 			shader = R_GetShaderByHandle( ent->e.customShader );
    339 		} else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) {
    340 			skin_t *skin;
    341 			int		j;
    342 
    343 			skin = R_GetSkinByHandle( ent->e.customSkin );
    344 
    345 			// match the surface name to something in the skin file
    346 			shader = tr.defaultShader;
    347 			for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
    348 				// the names have both been lowercased
    349 				if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
    350 					shader = skin->surfaces[j]->shader;
    351 					break;
    352 				}
    353 			}
    354 			if (shader == tr.defaultShader) {
    355 				ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name);
    356 			}
    357 			else if (shader->defaultShader) {
    358 				ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name);
    359 			}
    360 		} else if ( surface->numShaders <= 0 ) {
    361 			shader = tr.defaultShader;
    362 		} else {
    363 			md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders );
    364 			md3Shader += ent->e.skinNum % surface->numShaders;
    365 			shader = tr.shaders[ md3Shader->shaderIndex ];
    366 		}
    367 
    368 
    369 		// we will add shadows even if the main object isn't visible in the view
    370 
    371 		// stencil shadows can't do personal models unless I polyhedron clip
    372 		if ( !personalModel
    373 			&& r_shadows->integer == 2 
    374 			&& fogNum == 0
    375 			&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) 
    376 			&& shader->sort == SS_OPAQUE ) {
    377 			R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse );
    378 		}
    379 
    380 		// projection shadows work fine with personal models
    381 		if ( r_shadows->integer == 3
    382 			&& fogNum == 0
    383 			&& (ent->e.renderfx & RF_SHADOW_PLANE )
    384 			&& shader->sort == SS_OPAQUE ) {
    385 			R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse );
    386 		}
    387 
    388 		// don't add third_person objects if not viewing through a portal
    389 		if ( !personalModel ) {
    390 			R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse );
    391 		}
    392 
    393 		surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd );
    394 	}
    395 
    396 }
    397