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