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