Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

tr_model.c (18139B)


      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_models.c -- model loading and caching
     23 
     24 #include "tr_local.h"
     25 
     26 #define	LL(x) x=LittleLong(x)
     27 
     28 static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *name );
     29 static qboolean R_LoadMD4 (model_t *mod, void *buffer, const char *name );
     30 
     31 model_t	*loadmodel;
     32 
     33 /*
     34 ** R_GetModelByHandle
     35 */
     36 model_t	*R_GetModelByHandle( qhandle_t index ) {
     37 	model_t		*mod;
     38 
     39 	// out of range gets the defualt model
     40 	if ( index < 1 || index >= tr.numModels ) {
     41 		return tr.models[0];
     42 	}
     43 
     44 	mod = tr.models[index];
     45 
     46 	return mod;
     47 }
     48 
     49 //===============================================================================
     50 
     51 /*
     52 ** R_AllocModel
     53 */
     54 model_t *R_AllocModel( void ) {
     55 	model_t		*mod;
     56 
     57 	if ( tr.numModels == MAX_MOD_KNOWN ) {
     58 		return NULL;
     59 	}
     60 
     61 	mod = ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low );
     62 	mod->index = tr.numModels;
     63 	tr.models[tr.numModels] = mod;
     64 	tr.numModels++;
     65 
     66 	return mod;
     67 }
     68 
     69 /*
     70 ====================
     71 RE_RegisterModel
     72 
     73 Loads in a model for the given name
     74 
     75 Zero will be returned if the model fails to load.
     76 An entry will be retained for failed models as an
     77 optimization to prevent disk rescanning if they are
     78 asked for again.
     79 ====================
     80 */
     81 qhandle_t RE_RegisterModel( const char *name ) {
     82 	model_t		*mod;
     83 	unsigned	*buf;
     84 	int			lod;
     85 	int			ident;
     86 	qboolean	loaded;
     87 	qhandle_t	hModel;
     88 	int			numLoaded;
     89 
     90 	if ( !name || !name[0] ) {
     91 		ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" );
     92 		return 0;
     93 	}
     94 
     95 	if ( strlen( name ) >= MAX_QPATH ) {
     96 		Com_Printf( "Model name exceeds MAX_QPATH\n" );
     97 		return 0;
     98 	}
     99 
    100 	//
    101 	// search the currently loaded models
    102 	//
    103 	for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) {
    104 		mod = tr.models[hModel];
    105 		if ( !strcmp( mod->name, name ) ) {
    106 			if( mod->type == MOD_BAD ) {
    107 				return 0;
    108 			}
    109 			return hModel;
    110 		}
    111 	}
    112 
    113 	// allocate a new model_t
    114 
    115 	if ( ( mod = R_AllocModel() ) == NULL ) {
    116 		ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name);
    117 		return 0;
    118 	}
    119 
    120 	// only set the name after the model has been successfully loaded
    121 	Q_strncpyz( mod->name, name, sizeof( mod->name ) );
    122 
    123 
    124 	// make sure the render thread is stopped
    125 	R_SyncRenderThread();
    126 
    127 	mod->numLods = 0;
    128 
    129 	//
    130 	// load the files
    131 	//
    132 	numLoaded = 0;
    133 
    134 	for ( lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod-- ) {
    135 		char filename[1024];
    136 
    137 		strcpy( filename, name );
    138 
    139 		if ( lod != 0 ) {
    140 			char namebuf[80];
    141 
    142 			if ( strrchr( filename, '.' ) ) {
    143 				*strrchr( filename, '.' ) = 0;
    144 			}
    145 			sprintf( namebuf, "_%d.md3", lod );
    146 			strcat( filename, namebuf );
    147 		}
    148 
    149 		ri.FS_ReadFile( filename, (void **)&buf );
    150 		if ( !buf ) {
    151 			continue;
    152 		}
    153 		
    154 		loadmodel = mod;
    155 		
    156 		ident = LittleLong(*(unsigned *)buf);
    157 		if ( ident == MD4_IDENT ) {
    158 			loaded = R_LoadMD4( mod, buf, name );
    159 		} else {
    160 			if ( ident != MD3_IDENT ) {
    161 				ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name);
    162 				goto fail;
    163 			}
    164 
    165 			loaded = R_LoadMD3( mod, lod, buf, name );
    166 		}
    167 		
    168 		ri.FS_FreeFile (buf);
    169 
    170 		if ( !loaded ) {
    171 			if ( lod == 0 ) {
    172 				goto fail;
    173 			} else {
    174 				break;
    175 			}
    176 		} else {
    177 			mod->numLods++;
    178 			numLoaded++;
    179 			// if we have a valid model and are biased
    180 			// so that we won't see any higher detail ones,
    181 			// stop loading them
    182 //			if ( lod <= r_lodbias->integer ) {
    183 //				break;
    184 //			}
    185 		}
    186 	}
    187 
    188 	if ( numLoaded ) {
    189 		// duplicate into higher lod spots that weren't
    190 		// loaded, in case the user changes r_lodbias on the fly
    191 		for ( lod-- ; lod >= 0 ; lod-- ) {
    192 			mod->numLods++;
    193 			mod->md3[lod] = mod->md3[lod+1];
    194 		}
    195 
    196 		return mod->index;
    197 	}
    198 #ifdef _DEBUG
    199 	else {
    200 		ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name);
    201 	}
    202 #endif
    203 
    204 fail:
    205 	// we still keep the model_t around, so if the model name is asked for
    206 	// again, we won't bother scanning the filesystem
    207 	mod->type = MOD_BAD;
    208 	return 0;
    209 }
    210 
    211 
    212 /*
    213 =================
    214 R_LoadMD3
    215 =================
    216 */
    217 static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) {
    218 	int					i, j;
    219 	md3Header_t			*pinmodel;
    220     md3Frame_t			*frame;
    221 	md3Surface_t		*surf;
    222 	md3Shader_t			*shader;
    223 	md3Triangle_t		*tri;
    224 	md3St_t				*st;
    225 	md3XyzNormal_t		*xyz;
    226 	md3Tag_t			*tag;
    227 	int					version;
    228 	int					size;
    229 
    230 	pinmodel = (md3Header_t *)buffer;
    231 
    232 	version = LittleLong (pinmodel->version);
    233 	if (version != MD3_VERSION) {
    234 		ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n",
    235 				 mod_name, version, MD3_VERSION);
    236 		return qfalse;
    237 	}
    238 
    239 	mod->type = MOD_MESH;
    240 	size = LittleLong(pinmodel->ofsEnd);
    241 	mod->dataSize += size;
    242 	mod->md3[lod] = ri.Hunk_Alloc( size, h_low );
    243 
    244 	Com_Memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) );
    245 
    246     LL(mod->md3[lod]->ident);
    247     LL(mod->md3[lod]->version);
    248     LL(mod->md3[lod]->numFrames);
    249     LL(mod->md3[lod]->numTags);
    250     LL(mod->md3[lod]->numSurfaces);
    251     LL(mod->md3[lod]->ofsFrames);
    252     LL(mod->md3[lod]->ofsTags);
    253     LL(mod->md3[lod]->ofsSurfaces);
    254     LL(mod->md3[lod]->ofsEnd);
    255 
    256 	if ( mod->md3[lod]->numFrames < 1 ) {
    257 		ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name );
    258 		return qfalse;
    259 	}
    260     
    261 	// swap all the frames
    262     frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames );
    263     for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) {
    264     	frame->radius = LittleFloat( frame->radius );
    265         for ( j = 0 ; j < 3 ; j++ ) {
    266             frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
    267             frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
    268 	    	frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
    269         }
    270 	}
    271 
    272 	// swap all the tags
    273     tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags );
    274     for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) {
    275         for ( j = 0 ; j < 3 ; j++ ) {
    276 			tag->origin[j] = LittleFloat( tag->origin[j] );
    277 			tag->axis[0][j] = LittleFloat( tag->axis[0][j] );
    278 			tag->axis[1][j] = LittleFloat( tag->axis[1][j] );
    279 			tag->axis[2][j] = LittleFloat( tag->axis[2][j] );
    280         }
    281 	}
    282 
    283 	// swap all the surfaces
    284 	surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces );
    285 	for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) {
    286 
    287         LL(surf->ident);
    288         LL(surf->flags);
    289         LL(surf->numFrames);
    290         LL(surf->numShaders);
    291         LL(surf->numTriangles);
    292         LL(surf->ofsTriangles);
    293         LL(surf->numVerts);
    294         LL(surf->ofsShaders);
    295         LL(surf->ofsSt);
    296         LL(surf->ofsXyzNormals);
    297         LL(surf->ofsEnd);
    298 		
    299 		if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
    300 			ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)",
    301 				mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
    302 		}
    303 		if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
    304 			ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)",
    305 				mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
    306 		}
    307 	
    308 		// change to surface identifier
    309 		surf->ident = SF_MD3;
    310 
    311 		// lowercase the surface name so skin compares are faster
    312 		Q_strlwr( surf->name );
    313 
    314 		// strip off a trailing _1 or _2
    315 		// this is a crutch for q3data being a mess
    316 		j = strlen( surf->name );
    317 		if ( j > 2 && surf->name[j-2] == '_' ) {
    318 			surf->name[j-2] = 0;
    319 		}
    320 
    321         // register the shaders
    322         shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
    323         for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) {
    324             shader_t	*sh;
    325 
    326             sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue );
    327 			if ( sh->defaultShader ) {
    328 				shader->shaderIndex = 0;
    329 			} else {
    330 				shader->shaderIndex = sh->index;
    331 			}
    332         }
    333 
    334 		// swap all the triangles
    335 		tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
    336 		for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
    337 			LL(tri->indexes[0]);
    338 			LL(tri->indexes[1]);
    339 			LL(tri->indexes[2]);
    340 		}
    341 
    342 		// swap all the ST
    343         st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
    344         for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
    345             st->st[0] = LittleFloat( st->st[0] );
    346             st->st[1] = LittleFloat( st->st[1] );
    347         }
    348 
    349 		// swap all the XyzNormals
    350         xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
    351         for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) 
    352 		{
    353             xyz->xyz[0] = LittleShort( xyz->xyz[0] );
    354             xyz->xyz[1] = LittleShort( xyz->xyz[1] );
    355             xyz->xyz[2] = LittleShort( xyz->xyz[2] );
    356 
    357             xyz->normal = LittleShort( xyz->normal );
    358         }
    359 
    360 
    361 		// find the next surface
    362 		surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
    363 	}
    364     
    365 	return qtrue;
    366 }
    367 
    368 
    369 
    370 /*
    371 =================
    372 R_LoadMD4
    373 =================
    374 */
    375 static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) {
    376 	int					i, j, k, lodindex;
    377 	md4Header_t			*pinmodel, *md4;
    378     md4Frame_t			*frame;
    379 	md4LOD_t			*lod;
    380 	md4Surface_t		*surf;
    381 	md4Triangle_t		*tri;
    382 	md4Vertex_t			*v;
    383 	int					version;
    384 	int					size;
    385 	shader_t			*sh;
    386 	int					frameSize;
    387 
    388 	pinmodel = (md4Header_t *)buffer;
    389 
    390 	version = LittleLong (pinmodel->version);
    391 	if (version != MD4_VERSION) {
    392 		ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n",
    393 				 mod_name, version, MD4_VERSION);
    394 		return qfalse;
    395 	}
    396 
    397 	mod->type = MOD_MD4;
    398 	size = LittleLong(pinmodel->ofsEnd);
    399 	mod->dataSize += size;
    400 	md4 = mod->md4 = ri.Hunk_Alloc( size, h_low );
    401 
    402 	Com_Memcpy( md4, buffer, LittleLong(pinmodel->ofsEnd) );
    403 
    404     LL(md4->ident);
    405     LL(md4->version);
    406     LL(md4->numFrames);
    407     LL(md4->numBones);
    408     LL(md4->numLODs);
    409     LL(md4->ofsFrames);
    410     LL(md4->ofsLODs);
    411     LL(md4->ofsEnd);
    412 
    413 	if ( md4->numFrames < 1 ) {
    414 		ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name );
    415 		return qfalse;
    416 	}
    417 
    418     // we don't need to swap tags in the renderer, they aren't used
    419     
    420 	// swap all the frames
    421 	frameSize = (int)( &((md4Frame_t *)0)->bones[ md4->numBones ] );
    422     for ( i = 0 ; i < md4->numFrames ; i++, frame++) {
    423 	    frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize );
    424     	frame->radius = LittleFloat( frame->radius );
    425         for ( j = 0 ; j < 3 ; j++ ) {
    426             frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
    427             frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
    428 	    	frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
    429         }
    430 		for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) {
    431 			((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] );
    432 		}
    433 	}
    434 
    435 	// swap all the LOD's
    436 	lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs );
    437 	for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) {
    438 
    439 		// swap all the surfaces
    440 		surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces );
    441 		for ( i = 0 ; i < lod->numSurfaces ; i++) {
    442 			LL(surf->ident);
    443 			LL(surf->numTriangles);
    444 			LL(surf->ofsTriangles);
    445 			LL(surf->numVerts);
    446 			LL(surf->ofsVerts);
    447 			LL(surf->ofsEnd);
    448 			
    449 			if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
    450 				ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)",
    451 					mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
    452 			}
    453 			if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
    454 				ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)",
    455 					mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
    456 			}
    457 
    458 			// change to surface identifier
    459 			surf->ident = SF_MD4;
    460 
    461 			// lowercase the surface name so skin compares are faster
    462 			Q_strlwr( surf->name );
    463 		
    464 			// register the shaders
    465 			sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue );
    466 			if ( sh->defaultShader ) {
    467 				surf->shaderIndex = 0;
    468 			} else {
    469 				surf->shaderIndex = sh->index;
    470 			}
    471 
    472 			// swap all the triangles
    473 			tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
    474 			for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
    475 				LL(tri->indexes[0]);
    476 				LL(tri->indexes[1]);
    477 				LL(tri->indexes[2]);
    478 			}
    479 
    480 			// swap all the vertexes
    481 			// FIXME
    482 			// This makes TFC's skeletons work.  Shouldn't be necessary anymore, but left
    483 			// in for reference.
    484 			//v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12);
    485 			v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts);
    486 			for ( j = 0 ; j < surf->numVerts ; j++ ) {
    487 				v->normal[0] = LittleFloat( v->normal[0] );
    488 				v->normal[1] = LittleFloat( v->normal[1] );
    489 				v->normal[2] = LittleFloat( v->normal[2] );
    490 
    491 				v->texCoords[0] = LittleFloat( v->texCoords[0] );
    492 				v->texCoords[1] = LittleFloat( v->texCoords[1] );
    493 
    494 				v->numWeights = LittleLong( v->numWeights );
    495 
    496 				for ( k = 0 ; k < v->numWeights ; k++ ) {
    497 					v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex );
    498 					v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight );
    499 				   v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] );
    500 				   v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] );
    501 				   v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] );
    502 				}
    503 				// FIXME
    504 				// This makes TFC's skeletons work.  Shouldn't be necessary anymore, but left
    505 				// in for reference.
    506 				//v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 );
    507 				v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]);
    508 			}
    509 
    510 			// find the next surface
    511 			surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd );
    512 		}
    513 
    514 		// find the next LOD
    515 		lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd );
    516 	}
    517 
    518 	return qtrue;
    519 }
    520 
    521 
    522 
    523 
    524 //=============================================================================
    525 
    526 /*
    527 ** RE_BeginRegistration
    528 */
    529 void RE_BeginRegistration( glconfig_t *glconfigOut ) {
    530 
    531 	R_Init();
    532 
    533 	*glconfigOut = glConfig;
    534 
    535 	R_SyncRenderThread();
    536 
    537 	tr.viewCluster = -1;		// force markleafs to regenerate
    538 	R_ClearFlares();
    539 	RE_ClearScene();
    540 
    541 	tr.registered = qtrue;
    542 
    543 	// NOTE: this sucks, for some reason the first stretch pic is never drawn
    544 	// without this we'd see a white flash on a level load because the very
    545 	// first time the level shot would not be drawn
    546 	RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0);
    547 }
    548 
    549 //=============================================================================
    550 
    551 /*
    552 ===============
    553 R_ModelInit
    554 ===============
    555 */
    556 void R_ModelInit( void ) {
    557 	model_t		*mod;
    558 
    559 	// leave a space for NULL model
    560 	tr.numModels = 0;
    561 
    562 	mod = R_AllocModel();
    563 	mod->type = MOD_BAD;
    564 }
    565 
    566 
    567 /*
    568 ================
    569 R_Modellist_f
    570 ================
    571 */
    572 void R_Modellist_f( void ) {
    573 	int		i, j;
    574 	model_t	*mod;
    575 	int		total;
    576 	int		lods;
    577 
    578 	total = 0;
    579 	for ( i = 1 ; i < tr.numModels; i++ ) {
    580 		mod = tr.models[i];
    581 		lods = 1;
    582 		for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) {
    583 			if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) {
    584 				lods++;
    585 			}
    586 		}
    587 		ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name );
    588 		total += mod->dataSize;
    589 	}
    590 	ri.Printf( PRINT_ALL, "%8i : Total models\n", total );
    591 
    592 #if	0		// not working right with new hunk
    593 	if ( tr.world ) {
    594 		ri.Printf( PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name );
    595 	}
    596 #endif
    597 }
    598 
    599 
    600 //=============================================================================
    601 
    602 
    603 /*
    604 ================
    605 R_GetTag
    606 ================
    607 */
    608 static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) {
    609 	md3Tag_t		*tag;
    610 	int				i;
    611 
    612 	if ( frame >= mod->numFrames ) {
    613 		// it is possible to have a bad frame while changing models, so don't error
    614 		frame = mod->numFrames - 1;
    615 	}
    616 
    617 	tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags;
    618 	for ( i = 0 ; i < mod->numTags ; i++, tag++ ) {
    619 		if ( !strcmp( tag->name, tagName ) ) {
    620 			return tag;	// found it
    621 		}
    622 	}
    623 
    624 	return NULL;
    625 }
    626 
    627 /*
    628 ================
    629 R_LerpTag
    630 ================
    631 */
    632 int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, 
    633 					 float frac, const char *tagName ) {
    634 	md3Tag_t	*start, *end;
    635 	int		i;
    636 	float		frontLerp, backLerp;
    637 	model_t		*model;
    638 
    639 	model = R_GetModelByHandle( handle );
    640 	if ( !model->md3[0] ) {
    641 		AxisClear( tag->axis );
    642 		VectorClear( tag->origin );
    643 		return qfalse;
    644 	}
    645 
    646 	start = R_GetTag( model->md3[0], startFrame, tagName );
    647 	end = R_GetTag( model->md3[0], endFrame, tagName );
    648 	if ( !start || !end ) {
    649 		AxisClear( tag->axis );
    650 		VectorClear( tag->origin );
    651 		return qfalse;
    652 	}
    653 
    654 	frontLerp = frac;
    655 	backLerp = 1.0f - frac;
    656 
    657 	for ( i = 0 ; i < 3 ; i++ ) {
    658 		tag->origin[i] = start->origin[i] * backLerp +  end->origin[i] * frontLerp;
    659 		tag->axis[0][i] = start->axis[0][i] * backLerp +  end->axis[0][i] * frontLerp;
    660 		tag->axis[1][i] = start->axis[1][i] * backLerp +  end->axis[1][i] * frontLerp;
    661 		tag->axis[2][i] = start->axis[2][i] * backLerp +  end->axis[2][i] * frontLerp;
    662 	}
    663 	VectorNormalize( tag->axis[0] );
    664 	VectorNormalize( tag->axis[1] );
    665 	VectorNormalize( tag->axis[2] );
    666 	return qtrue;
    667 }
    668 
    669 
    670 /*
    671 ====================
    672 R_ModelBounds
    673 ====================
    674 */
    675 void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) {
    676 	model_t		*model;
    677 	md3Header_t	*header;
    678 	md3Frame_t	*frame;
    679 
    680 	model = R_GetModelByHandle( handle );
    681 
    682 	if ( model->bmodel ) {
    683 		VectorCopy( model->bmodel->bounds[0], mins );
    684 		VectorCopy( model->bmodel->bounds[1], maxs );
    685 		return;
    686 	}
    687 
    688 	if ( !model->md3[0] ) {
    689 		VectorClear( mins );
    690 		VectorClear( maxs );
    691 		return;
    692 	}
    693 
    694 	header = model->md3[0];
    695 
    696 	frame = (md3Frame_t *)( (byte *)header + header->ofsFrames );
    697 
    698 	VectorCopy( frame->bounds[0], mins );
    699 	VectorCopy( frame->bounds[1], maxs );
    700 }