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 Quake III Arena source code; 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 
ProjectRadius(float r,vec3_t location)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 */
R_CullModel(md3Header_t * header,trRefEntity_t * ent)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 */
R_ComputeLOD(trRefEntity_t * ent)166 int R_ComputeLOD( trRefEntity_t *ent ) {
167 	float radius;
168 	float flod, lodscale;
169 	float projectedRadius;
170 	md3Frame_t *frame;
171 #ifdef RAVENMD4
172 	mdrHeader_t *mdr;
173 	mdrFrame_t *mdrframe;
174 #endif
175 	int lod;
176 
177 	if ( tr.currentModel->numLods < 2 )
178 	{
179 		// model has only 1 LOD level, skip computations and bias
180 		lod = 0;
181 	}
182 	else
183 	{
184 		// multiple LODs exist, so compute projected bounding sphere
185 		// and use that as a criteria for selecting LOD
186 
187 #ifdef RAVENMD4
188 		// This is an MDR model.
189 
190 		if(tr.currentModel->md4)
191 		{
192 			int frameSize;
193 			mdr = (mdrHeader_t *) tr.currentModel->md4;
194 			frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]);
195 
196 			mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame);
197 
198 			radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]);
199 		}
200 		else
201 #endif
202 		{
203 			frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames );
204 
205 			frame += ent->e.frame;
206 
207 			radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] );
208 		}
209 
210 		if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 )
211 		{
212 			lodscale = r_lodscale->value;
213 			if (lodscale > 20) lodscale = 20;
214 			flod = 1.0f - projectedRadius * lodscale;
215 		}
216 		else
217 		{
218 			// object intersects near view plane, e.g. view weapon
219 			flod = 0;
220 		}
221 
222 		flod *= tr.currentModel->numLods;
223 		lod = myftol( flod );
224 
225 		if ( lod < 0 )
226 		{
227 			lod = 0;
228 		}
229 		else if ( lod >= tr.currentModel->numLods )
230 		{
231 			lod = tr.currentModel->numLods - 1;
232 		}
233 	}
234 
235 	lod += r_lodbias->integer;
236 
237 	if ( lod >= tr.currentModel->numLods )
238 		lod = tr.currentModel->numLods - 1;
239 	if ( lod < 0 )
240 		lod = 0;
241 
242 	return lod;
243 }
244 
245 /*
246 =================
247 R_ComputeFogNum
248 
249 =================
250 */
R_ComputeFogNum(md3Header_t * header,trRefEntity_t * ent)251 int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) {
252 	int				i, j;
253 	fog_t			*fog;
254 	md3Frame_t		*md3Frame;
255 	vec3_t			localOrigin;
256 
257 	if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
258 		return 0;
259 	}
260 
261 	// FIXME: non-normalized axis issues
262 	md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame;
263 	VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin );
264 	for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
265 		fog = &tr.world->fogs[i];
266 		for ( j = 0 ; j < 3 ; j++ ) {
267 			if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) {
268 				break;
269 			}
270 			if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) {
271 				break;
272 			}
273 		}
274 		if ( j == 3 ) {
275 			return i;
276 		}
277 	}
278 
279 	return 0;
280 }
281 
282 /*
283 =================
284 R_AddMD3Surfaces
285 
286 =================
287 */
R_AddMD3Surfaces(trRefEntity_t * ent)288 void R_AddMD3Surfaces( trRefEntity_t *ent ) {
289 	int				i;
290 	md3Header_t		*header = NULL;
291 	md3Surface_t	*surface = NULL;
292 	md3Shader_t		*md3Shader = NULL;
293 	shader_t		*shader = NULL;
294 	int				cull;
295 	int				lod;
296 	int				fogNum;
297 	qboolean		personalModel;
298 
299 	// don't add third_person objects if not in a portal
300 	personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
301 
302 	if ( ent->e.renderfx & RF_WRAP_FRAMES ) {
303 		ent->e.frame %= tr.currentModel->md3[0]->numFrames;
304 		ent->e.oldframe %= tr.currentModel->md3[0]->numFrames;
305 	}
306 
307 	//
308 	// Validate the frames so there is no chance of a crash.
309 	// This will write directly into the entity structure, so
310 	// when the surfaces are rendered, they don't need to be
311 	// range checked again.
312 	//
313 	if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames)
314 		|| (ent->e.frame < 0)
315 		|| (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames)
316 		|| (ent->e.oldframe < 0) ) {
317 			ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n",
318 				ent->e.oldframe, ent->e.frame,
319 				tr.currentModel->name );
320 			ent->e.frame = 0;
321 			ent->e.oldframe = 0;
322 	}
323 
324 	//
325 	// compute LOD
326 	//
327 	lod = R_ComputeLOD( ent );
328 
329 	header = tr.currentModel->md3[lod];
330 
331 	//
332 	// cull the entire model if merged bounding box of both frames
333 	// is outside the view frustum.
334 	//
335 	cull = R_CullModel ( header, ent );
336 	if ( cull == CULL_OUT ) {
337 		return;
338 	}
339 
340 	//
341 	// set up lighting now that we know we aren't culled
342 	//
343 	if ( !personalModel || r_shadows->integer > 1 ) {
344 		R_SetupEntityLighting( &tr.refdef, ent );
345 	}
346 
347 	//
348 	// see if we are in a fog volume
349 	//
350 	fogNum = R_ComputeFogNum( header, ent );
351 
352 	//
353 	// draw all surfaces
354 	//
355 	surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces );
356 	for ( i = 0 ; i < header->numSurfaces ; i++ ) {
357 
358 		if ( ent->e.customShader ) {
359 			shader = R_GetShaderByHandle( ent->e.customShader );
360 		} else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) {
361 			skin_t *skin;
362 			int		j;
363 
364 			skin = R_GetSkinByHandle( ent->e.customSkin );
365 
366 			// match the surface name to something in the skin file
367 			shader = tr.defaultShader;
368 			for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
369 				// the names have both been lowercased
370 				if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
371 					shader = skin->surfaces[j]->shader;
372 					break;
373 				}
374 			}
375 			if (shader == tr.defaultShader) {
376 				ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name);
377 			}
378 			else if (shader->defaultShader) {
379 				ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name);
380 			}
381 		} else if ( surface->numShaders <= 0 ) {
382 			shader = tr.defaultShader;
383 		} else {
384 			md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders );
385 			md3Shader += ent->e.skinNum % surface->numShaders;
386 			shader = tr.shaders[ md3Shader->shaderIndex ];
387 		}
388 
389 
390 		// we will add shadows even if the main object isn't visible in the view
391 
392 		// stencil shadows can't do personal models unless I polyhedron clip
393 		if ( !personalModel
394 			&& r_shadows->integer == 2
395 			&& fogNum == 0
396 			&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
397 			&& shader->sort == SS_OPAQUE ) {
398 			R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse );
399 		}
400 
401 		// projection shadows work fine with personal models
402 		if ( r_shadows->integer == 3
403 			&& fogNum == 0
404 			&& (ent->e.renderfx & RF_SHADOW_PLANE )
405 			&& shader->sort == SS_OPAQUE ) {
406 			R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse );
407 		}
408 
409 		// don't add third_person objects if not viewing through a portal
410 		if ( !personalModel ) {
411 			R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse );
412 		}
413 
414 		surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd );
415 	}
416 
417 }
418 
419