1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30 #include "idlib/math/Interpolate.h"
31 #include "framework/Game.h"
32 #include "renderer/VertexCache.h"
33 #include "renderer/RenderWorld_local.h"
34 #include "ui/Window.h"
35
36 #include "renderer/tr_local.h"
37
38 static const float CHECK_BOUNDS_EPSILON = 1.0f;
39
40 /*
41 ===========================================================================================
42
43 VERTEX CACHE GENERATORS
44
45 ===========================================================================================
46 */
47
48 /*
49 ==================
50 R_CreateAmbientCache
51
52 Create it if needed
53 ==================
54 */
R_CreateAmbientCache(srfTriangles_t * tri,bool needsLighting)55 bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) {
56 if ( tri->ambientCache ) {
57 return true;
58 }
59 // we are going to use it for drawing, so make sure we have the tangents and normals
60 if ( needsLighting && !tri->tangentsCalculated ) {
61 R_DeriveTangents( tri );
62 }
63
64 vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache );
65 if ( !tri->ambientCache ) {
66 return false;
67 }
68 return true;
69 }
70
71 /*
72 ==================
73 R_CreateLightingCache
74
75 Returns false if the cache couldn't be allocated, in which case the surface should be skipped.
76 ==================
77 */
R_CreateLightingCache(const idRenderEntityLocal * ent,const idRenderLightLocal * light,srfTriangles_t * tri)78 bool R_CreateLightingCache( const idRenderEntityLocal *ent, const idRenderLightLocal *light, srfTriangles_t *tri ) {
79 idVec3 localLightOrigin;
80
81 // fogs and blends don't need light vectors
82 if ( light->lightShader->IsFogLight() || light->lightShader->IsBlendLight() ) {
83 return true;
84 }
85
86 // not needed if we have vertex programs
87 if ( tr.backEndRendererHasVertexPrograms ) {
88 return true;
89 }
90
91 R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
92
93 int size = tri->ambientSurface->numVerts * sizeof( lightingCache_t );
94 lightingCache_t *cache = (lightingCache_t *)_alloca16( size );
95
96 #if 1
97
98 SIMDProcessor->CreateTextureSpaceLightVectors( &cache[0].localLightVector, localLightOrigin,
99 tri->ambientSurface->verts, tri->ambientSurface->numVerts, tri->indexes, tri->numIndexes );
100
101 #else
102
103 bool *used = (bool *)_alloca16( tri->ambientSurface->numVerts * sizeof( used[0] ) );
104 memset( used, 0, tri->ambientSurface->numVerts * sizeof( used[0] ) );
105
106 // because the interaction may be a very small subset of the full surface,
107 // it makes sense to only deal with the verts used
108 for ( int j = 0; j < tri->numIndexes; j++ ) {
109 int i = tri->indexes[j];
110 if ( used[i] ) {
111 continue;
112 }
113 used[i] = true;
114
115 idVec3 lightDir;
116 const idDrawVert *v;
117
118 v = &tri->ambientSurface->verts[i];
119
120 lightDir = localLightOrigin - v->xyz;
121
122 cache[i].localLightVector[0] = lightDir * v->tangents[0];
123 cache[i].localLightVector[1] = lightDir * v->tangents[1];
124 cache[i].localLightVector[2] = lightDir * v->normal;
125 }
126
127 #endif
128
129 vertexCache.Alloc( cache, size, &tri->lightingCache );
130 if ( !tri->lightingCache ) {
131 return false;
132 }
133 return true;
134 }
135
136 /*
137 ==================
138 R_CreatePrivateShadowCache
139
140 This is used only for a specific light
141 ==================
142 */
R_CreatePrivateShadowCache(srfTriangles_t * tri)143 void R_CreatePrivateShadowCache( srfTriangles_t *tri ) {
144 if ( !tri->shadowVertexes ) {
145 return;
146 }
147
148 vertexCache.Alloc( tri->shadowVertexes, tri->numVerts * sizeof( *tri->shadowVertexes ), &tri->shadowCache );
149 }
150
151 /*
152 ==================
153 R_CreateVertexProgramShadowCache
154
155 This is constant for any number of lights, the vertex program
156 takes care of projecting the verts to infinity.
157 ==================
158 */
R_CreateVertexProgramShadowCache(srfTriangles_t * tri)159 void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) {
160 if ( tri->verts == NULL ) {
161 return;
162 }
163
164 shadowCache_t *temp = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) );
165
166 #if 1
167
168 SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts );
169
170 #else
171
172 int numVerts = tri->numVerts;
173 const idDrawVert *verts = tri->verts;
174 for ( int i = 0; i < numVerts; i++ ) {
175 const float *v = verts[i].xyz.ToFloatPtr();
176 temp[i*2+0].xyz[0] = v[0];
177 temp[i*2+1].xyz[0] = v[0];
178 temp[i*2+0].xyz[1] = v[1];
179 temp[i*2+1].xyz[1] = v[1];
180 temp[i*2+0].xyz[2] = v[2];
181 temp[i*2+1].xyz[2] = v[2];
182 temp[i*2+0].xyz[3] = 1.0f; // on the model surface
183 temp[i*2+1].xyz[3] = 0.0f; // will be projected to infinity
184 }
185
186 #endif
187
188 vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache );
189 }
190
191 /*
192 ==================
193 R_SkyboxTexGen
194 ==================
195 */
R_SkyboxTexGen(drawSurf_t * surf,const idVec3 & viewOrg)196 void R_SkyboxTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
197 int i;
198 idVec3 localViewOrigin;
199
200 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
201
202 int numVerts = surf->geo->numVerts;
203 int size = numVerts * sizeof( idVec3 );
204 idVec3 *texCoords = (idVec3 *) _alloca16( size );
205
206 const idDrawVert *verts = surf->geo->verts;
207 for ( i = 0; i < numVerts; i++ ) {
208 texCoords[i][0] = verts[i].xyz[0] - localViewOrigin[0];
209 texCoords[i][1] = verts[i].xyz[1] - localViewOrigin[1];
210 texCoords[i][2] = verts[i].xyz[2] - localViewOrigin[2];
211 }
212
213 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
214 }
215
216 /*
217 ==================
218 R_WobbleskyTexGen
219 ==================
220 */
R_WobbleskyTexGen(drawSurf_t * surf,const idVec3 & viewOrg)221 void R_WobbleskyTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
222 int i;
223 idVec3 localViewOrigin;
224
225 const int *parms = surf->material->GetTexGenRegisters();
226
227 float wobbleDegrees = surf->shaderRegisters[ parms[0] ];
228 float wobbleSpeed = surf->shaderRegisters[ parms[1] ];
229 float rotateSpeed = surf->shaderRegisters[ parms[2] ];
230
231 wobbleDegrees = wobbleDegrees * idMath::PI / 180;
232 wobbleSpeed = wobbleSpeed * 2 * idMath::PI / 60;
233 rotateSpeed = rotateSpeed * 2 * idMath::PI / 60;
234
235 // very ad-hoc "wobble" transform
236 float transform[16];
237 float a = tr.viewDef->floatTime * wobbleSpeed;
238 float s = sin( a ) * sin( wobbleDegrees );
239 float c = cos( a ) * sin( wobbleDegrees );
240 float z = cos( wobbleDegrees );
241
242 idVec3 axis[3];
243
244 axis[2][0] = c;
245 axis[2][1] = s;
246 axis[2][2] = z;
247
248 axis[1][0] = -sin( a * 2 ) * sin( wobbleDegrees );
249 axis[1][2] = -s * sin( wobbleDegrees );
250 axis[1][1] = sqrt( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) );
251
252 // make the second vector exactly perpendicular to the first
253 axis[1] -= ( axis[2] * axis[1] ) * axis[2];
254 axis[1].Normalize();
255
256 // construct the third with a cross
257 axis[0].Cross( axis[1], axis[2] );
258
259 // add the rotate
260 s = sin( rotateSpeed * tr.viewDef->floatTime );
261 c = cos( rotateSpeed * tr.viewDef->floatTime );
262
263 transform[0] = axis[0][0] * c + axis[1][0] * s;
264 transform[4] = axis[0][1] * c + axis[1][1] * s;
265 transform[8] = axis[0][2] * c + axis[1][2] * s;
266
267 transform[1] = axis[1][0] * c - axis[0][0] * s;
268 transform[5] = axis[1][1] * c - axis[0][1] * s;
269 transform[9] = axis[1][2] * c - axis[0][2] * s;
270
271 transform[2] = axis[2][0];
272 transform[6] = axis[2][1];
273 transform[10] = axis[2][2];
274
275 transform[3] = transform[7] = transform[11] = 0.0f;
276 transform[12] = transform[13] = transform[14] = 0.0f;
277
278 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
279
280 int numVerts = surf->geo->numVerts;
281 int size = numVerts * sizeof( idVec3 );
282 idVec3 *texCoords = (idVec3 *) _alloca16( size );
283
284 const idDrawVert *verts = surf->geo->verts;
285 for ( i = 0; i < numVerts; i++ ) {
286 idVec3 v;
287
288 v[0] = verts[i].xyz[0] - localViewOrigin[0];
289 v[1] = verts[i].xyz[1] - localViewOrigin[1];
290 v[2] = verts[i].xyz[2] - localViewOrigin[2];
291
292 R_LocalPointToGlobal( transform, v, texCoords[i] );
293 }
294
295 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
296 }
297
298 /*
299 =================
300 R_SpecularTexGen
301
302 Calculates the specular coordinates for cards without vertex programs.
303 =================
304 */
R_SpecularTexGen(drawSurf_t * surf,const idVec3 & globalLightOrigin,const idVec3 & viewOrg)305 static void R_SpecularTexGen( drawSurf_t *surf, const idVec3 &globalLightOrigin, const idVec3 &viewOrg ) {
306 const srfTriangles_t *tri;
307 idVec3 localLightOrigin;
308 idVec3 localViewOrigin;
309
310 R_GlobalPointToLocal( surf->space->modelMatrix, globalLightOrigin, localLightOrigin );
311 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
312
313 tri = surf->geo;
314
315 // FIXME: change to 3 component?
316 int size = tri->numVerts * sizeof( idVec4 );
317 idVec4 *texCoords = (idVec4 *) _alloca16( size );
318
319 #if 1
320
321 SIMDProcessor->CreateSpecularTextureCoords( texCoords, localLightOrigin, localViewOrigin,
322 tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
323
324 #else
325
326 bool *used = (bool *)_alloca16( tri->numVerts * sizeof( used[0] ) );
327 memset( used, 0, tri->numVerts * sizeof( used[0] ) );
328
329 // because the interaction may be a very small subset of the full surface,
330 // it makes sense to only deal with the verts used
331 for ( int j = 0; j < tri->numIndexes; j++ ) {
332 int i = tri->indexes[j];
333 if ( used[i] ) {
334 continue;
335 }
336 used[i] = true;
337
338 float ilength;
339
340 const idDrawVert *v = &tri->verts[i];
341
342 idVec3 lightDir = localLightOrigin - v->xyz;
343 idVec3 viewDir = localViewOrigin - v->xyz;
344
345 ilength = idMath::RSqrt( lightDir * lightDir );
346 lightDir[0] *= ilength;
347 lightDir[1] *= ilength;
348 lightDir[2] *= ilength;
349
350 ilength = idMath::RSqrt( viewDir * viewDir );
351 viewDir[0] *= ilength;
352 viewDir[1] *= ilength;
353 viewDir[2] *= ilength;
354
355 lightDir += viewDir;
356
357 texCoords[i][0] = lightDir * v->tangents[0];
358 texCoords[i][1] = lightDir * v->tangents[1];
359 texCoords[i][2] = lightDir * v->normal;
360 texCoords[i][3] = 1;
361 }
362
363 #endif
364
365 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
366 }
367
368
369 //=======================================================================================================
370
371 /*
372 =============
373 R_SetEntityDefViewEntity
374
375 If the entityDef isn't already on the viewEntity list, create
376 a viewEntity and add it to the list with an empty scissor rect.
377
378 This does not instantiate dynamic models for the entity yet.
379 =============
380 */
R_SetEntityDefViewEntity(idRenderEntityLocal * def)381 viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) {
382 viewEntity_t *vModel;
383
384 if ( def->viewCount == tr.viewCount ) {
385 return def->viewEntity;
386 }
387 def->viewCount = tr.viewCount;
388
389 // set the model and modelview matricies
390 vModel = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *vModel ) );
391 vModel->entityDef = def;
392
393 // the scissorRect will be expanded as the model bounds is accepted into visible portal chains
394 vModel->scissorRect.Clear();
395
396 // copy the model and weapon depth hack for back-end use
397 vModel->modelDepthHack = def->parms.modelDepthHack;
398 vModel->weaponDepthHack = def->parms.weaponDepthHack;
399
400 R_AxisToModelMatrix( def->parms.axis, def->parms.origin, vModel->modelMatrix );
401
402 // we may not have a viewDef if we are just creating shadows at entity creation time
403 if ( tr.viewDef ) {
404 myGlMultMatrix( vModel->modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, vModel->modelViewMatrix );
405
406 vModel->next = tr.viewDef->viewEntitys;
407 tr.viewDef->viewEntitys = vModel;
408 }
409
410 def->viewEntity = vModel;
411
412 return vModel;
413 }
414
415 /*
416 ====================
417 R_TestPointInViewLight
418 ====================
419 */
420 static const float INSIDE_LIGHT_FRUSTUM_SLOP = 32;
421 // this needs to be greater than the dist from origin to corner of near clip plane
R_TestPointInViewLight(const idVec3 & org,const idRenderLightLocal * light)422 static bool R_TestPointInViewLight( const idVec3 &org, const idRenderLightLocal *light ) {
423 int i;
424 idVec3 local;
425
426 for ( i = 0 ; i < 6 ; i++ ) {
427 float d = light->frustum[i].Distance( org );
428 if ( d > INSIDE_LIGHT_FRUSTUM_SLOP ) {
429 return false;
430 }
431 }
432
433 return true;
434 }
435
436 /*
437 ===================
438 R_PointInFrustum
439
440 Assumes positive sides face outward
441 ===================
442 */
R_PointInFrustum(idVec3 & p,idPlane * planes,int numPlanes)443 static bool R_PointInFrustum( idVec3 &p, idPlane *planes, int numPlanes ) {
444 for ( int i = 0 ; i < numPlanes ; i++ ) {
445 float d = planes[i].Distance( p );
446 if ( d > 0 ) {
447 return false;
448 }
449 }
450 return true;
451 }
452
453 /*
454 =============
455 R_SetLightDefViewLight
456
457 If the lightDef isn't already on the viewLight list, create
458 a viewLight and add it to the list with an empty scissor rect.
459 =============
460 */
R_SetLightDefViewLight(idRenderLightLocal * light)461 viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *light ) {
462 viewLight_t *vLight;
463
464 if ( light->viewCount == tr.viewCount ) {
465 return light->viewLight;
466 }
467 light->viewCount = tr.viewCount;
468
469 // add to the view light chain
470 vLight = (viewLight_t *)R_ClearedFrameAlloc( sizeof( *vLight ) );
471 vLight->lightDef = light;
472
473 // the scissorRect will be expanded as the light bounds is accepted into visible portal chains
474 vLight->scissorRect.Clear();
475
476 // calculate the shadow cap optimization states
477 vLight->viewInsideLight = R_TestPointInViewLight( tr.viewDef->renderView.vieworg, light );
478 if ( !vLight->viewInsideLight ) {
479 vLight->viewSeesShadowPlaneBits = 0;
480 for ( int i = 0 ; i < light->numShadowFrustums ; i++ ) {
481 float d = light->shadowFrustums[i].planes[5].Distance( tr.viewDef->renderView.vieworg );
482 if ( d < INSIDE_LIGHT_FRUSTUM_SLOP ) {
483 vLight->viewSeesShadowPlaneBits|= 1 << i;
484 }
485 }
486 } else {
487 // this should not be referenced in this case
488 vLight->viewSeesShadowPlaneBits = 63;
489 }
490
491 // see if the light center is in view, which will allow us to cull invisible shadows
492 vLight->viewSeesGlobalLightOrigin = R_PointInFrustum( light->globalLightOrigin, tr.viewDef->frustum, 4 );
493
494 // copy data used by backend
495 vLight->globalLightOrigin = light->globalLightOrigin;
496 vLight->lightProject[0] = light->lightProject[0];
497 vLight->lightProject[1] = light->lightProject[1];
498 vLight->lightProject[2] = light->lightProject[2];
499 vLight->lightProject[3] = light->lightProject[3];
500 vLight->fogPlane = light->frustum[5];
501 vLight->frustumTris = light->frustumTris;
502 vLight->falloffImage = light->falloffImage;
503 vLight->lightShader = light->lightShader;
504 vLight->shaderRegisters = NULL; // allocated and evaluated in R_AddLightSurfaces
505
506 // link the view light
507 vLight->next = tr.viewDef->viewLights;
508 tr.viewDef->viewLights = vLight;
509
510 light->viewLight = vLight;
511
512 return vLight;
513 }
514
515 /*
516 =================
517 idRenderWorldLocal::CreateLightDefInteractions
518
519 When a lightDef is determined to effect the view (contact the frustum and non-0 light), it will check to
520 make sure that it has interactions for all the entityDefs that it might possibly contact.
521
522 This does not guarantee that all possible interactions for this light are generated, only that
523 the ones that may effect the current view are generated. so it does need to be called every view.
524
525 This does not cause entityDefs to create dynamic models, all work is done on the referenceBounds.
526
527 All entities that have non-empty interactions with viewLights will
528 have viewEntities made for them and be put on the viewEntity list,
529 even if their surfaces aren't visible, because they may need to cast shadows.
530
531 Interactions are usually removed when a entityDef or lightDef is modified, unless the change
532 is known to not effect them, so there is no danger of getting a stale interaction, we just need to
533 check that needed ones are created.
534
535 An interaction can be at several levels:
536
537 Don't interact (but share an area) (numSurfaces = 0)
538 Entity reference bounds touches light frustum, but surfaces haven't been generated (numSurfaces = -1)
539 Shadow surfaces have been generated, but light surfaces have not. The shadow surface may still be empty due to bounds being conservative.
540 Both shadow and light surfaces have been generated. Either or both surfaces may still be empty due to conservative bounds.
541
542 =================
543 */
CreateLightDefInteractions(idRenderLightLocal * ldef)544 void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef ) {
545 areaReference_t *eref;
546 areaReference_t *lref;
547 idRenderEntityLocal *edef;
548 portalArea_t *area;
549 idInteraction *inter;
550
551 for ( lref = ldef->references ; lref ; lref = lref->ownerNext ) {
552 area = lref->area;
553
554 // check all the models in this area
555 for ( eref = area->entityRefs.areaNext ; eref != &area->entityRefs ; eref = eref->areaNext ) {
556 edef = eref->entity;
557
558 // if the entity doesn't have any light-interacting surfaces, we could skip this,
559 // but we don't want to instantiate dynamic models yet, so we can't check that on
560 // most things
561
562 // if the entity isn't viewed
563 if ( tr.viewDef && edef->viewCount != tr.viewCount ) {
564 // if the light doesn't cast shadows, skip
565 if ( !ldef->lightShader->LightCastsShadows() ) {
566 continue;
567 }
568 // if we are suppressing its shadow in this view, skip
569 if ( !r_skipSuppress.GetBool() ) {
570 if ( edef->parms.suppressShadowInViewID && edef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) {
571 continue;
572 }
573 if ( edef->parms.suppressShadowInLightID && edef->parms.suppressShadowInLightID == ldef->parms.lightId ) {
574 continue;
575 }
576 }
577 }
578
579 // some big outdoor meshes are flagged to not create any dynamic interactions
580 // when the level designer knows that nearby moving lights shouldn't actually hit them
581 if ( edef->parms.noDynamicInteractions && edef->world->generateAllInteractionsCalled ) {
582 continue;
583 }
584
585 // if any of the edef's interaction match this light, we don't
586 // need to consider it.
587 if ( r_useInteractionTable.GetBool() && this->interactionTable ) {
588 // allocating these tables may take several megs on big maps, but it saves 3% to 5% of
589 // the CPU time. The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
590 int index = ldef->index * this->interactionTableWidth + edef->index;
591 inter = this->interactionTable[ index ];
592 if ( inter ) {
593 // if this entity wasn't in view already, the scissor rect will be empty,
594 // so it will only be used for shadow casting
595 if ( !inter->IsEmpty() ) {
596 R_SetEntityDefViewEntity( edef );
597 }
598 continue;
599 }
600 } else {
601 // scan the doubly linked lists, which may have several dozen entries
602
603 // we could check either model refs or light refs for matches, but it is
604 // assumed that there will be less lights in an area than models
605 // so the entity chains should be somewhat shorter (they tend to be fairly close).
606 for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
607 if ( inter->lightDef == ldef ) {
608 break;
609 }
610 }
611
612 // if we already have an interaction, we don't need to do anything
613 if ( inter != NULL ) {
614 // if this entity wasn't in view already, the scissor rect will be empty,
615 // so it will only be used for shadow casting
616 if ( !inter->IsEmpty() ) {
617 R_SetEntityDefViewEntity( edef );
618 }
619 continue;
620 }
621 }
622
623 //
624 // create a new interaction, but don't do any work other than bbox to frustum culling
625 //
626 idInteraction *inter = idInteraction::AllocAndLink( edef, ldef );
627
628 // do a check of the entity reference bounds against the light frustum,
629 // trying to avoid creating a viewEntity if it hasn't been already
630 float modelMatrix[16];
631 float *m;
632
633 if ( edef->viewCount == tr.viewCount ) {
634 m = edef->viewEntity->modelMatrix;
635 } else {
636 R_AxisToModelMatrix( edef->parms.axis, edef->parms.origin, modelMatrix );
637 m = modelMatrix;
638 }
639
640 if ( R_CullLocalBox( edef->referenceBounds, m, 6, ldef->frustum ) ) {
641 inter->MakeEmpty();
642 continue;
643 }
644
645 // we will do a more precise per-surface check when we are checking the entity
646
647 // if this entity wasn't in view already, the scissor rect will be empty,
648 // so it will only be used for shadow casting
649 R_SetEntityDefViewEntity( edef );
650 }
651 }
652 }
653
654 //===============================================================================================================
655
656 /*
657 =================
658 R_LinkLightSurf
659 =================
660 */
R_LinkLightSurf(const drawSurf_t ** link,const srfTriangles_t * tri,const viewEntity_t * space,const idRenderLightLocal * light,const idMaterial * shader,const idScreenRect & scissor,bool viewInsideShadow)661 void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
662 const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow ) {
663 drawSurf_t *drawSurf;
664
665 if ( !space ) {
666 space = &tr.viewDef->worldSpace;
667 }
668
669 drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
670
671 drawSurf->geo = tri;
672 drawSurf->space = space;
673 drawSurf->material = shader;
674 drawSurf->scissorRect = scissor;
675 drawSurf->dsFlags = 0;
676 if ( viewInsideShadow ) {
677 drawSurf->dsFlags |= DSF_VIEW_INSIDE_SHADOW;
678 }
679
680 if ( !shader ) {
681 // shadows won't have a shader
682 drawSurf->shaderRegisters = NULL;
683 } else {
684 // process the shader expressions for conditionals / color / texcoords
685 const float *constRegs = shader->ConstantRegisters();
686 if ( constRegs ) {
687 // this shader has only constants for parameters
688 drawSurf->shaderRegisters = constRegs;
689 } else {
690 // FIXME: share with the ambient surface?
691 float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
692 drawSurf->shaderRegisters = regs;
693 shader->EvaluateRegisters( regs, space->entityDef->parms.shaderParms, tr.viewDef, space->entityDef->parms.referenceSound );
694 }
695
696 // calculate the specular coordinates if we aren't using vertex programs
697 if ( !tr.backEndRendererHasVertexPrograms && !r_skipSpecular.GetBool() ) {
698 R_SpecularTexGen( drawSurf, light->globalLightOrigin, tr.viewDef->renderView.vieworg );
699 // if we failed to allocate space for the specular calculations, drop the surface
700 if ( !drawSurf->dynamicTexCoords ) {
701 return;
702 }
703 }
704 }
705
706 // actually link it in
707 drawSurf->nextOnLight = *link;
708 *link = drawSurf;
709 }
710
711 /*
712 ======================
713 R_ClippedLightScissorRectangle
714 ======================
715 */
R_ClippedLightScissorRectangle(viewLight_t * vLight)716 idScreenRect R_ClippedLightScissorRectangle( viewLight_t *vLight ) {
717 int i, j;
718 const idRenderLightLocal *light = vLight->lightDef;
719 idScreenRect r;
720 idFixedWinding w;
721
722 r.Clear();
723
724 for ( i = 0 ; i < 6 ; i++ ) {
725 const idWinding *ow = light->frustumWindings[i];
726
727 // projected lights may have one of the frustums degenerated
728 if ( !ow ) {
729 continue;
730 }
731
732 // the light frustum planes face out from the light,
733 // so the planes that have the view origin on the negative
734 // side will be the "back" faces of the light, which must have
735 // some fragment inside the portalStack to be visible
736 if ( light->frustum[i].Distance( tr.viewDef->renderView.vieworg ) >= 0 ) {
737 continue;
738 }
739
740 w = *ow;
741
742 // now check the winding against each of the frustum planes
743 for ( j = 0; j < 5; j++ ) {
744 if ( !w.ClipInPlace( -tr.viewDef->frustum[j] ) ) {
745 break;
746 }
747 }
748
749 // project these points to the screen and add to bounds
750 for ( j = 0; j < w.GetNumPoints(); j++ ) {
751 idPlane eye, clip;
752 idVec3 ndc;
753
754 R_TransformModelToClip( w[j].ToVec3(), tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
755
756 if ( clip[3] <= 0.01f ) {
757 clip[3] = 0.01f;
758 }
759
760 R_TransformClipToDevice( clip, tr.viewDef, ndc );
761
762 float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
763 float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
764
765 if ( windowX > tr.viewDef->scissor.x2 ) {
766 windowX = tr.viewDef->scissor.x2;
767 } else if ( windowX < tr.viewDef->scissor.x1 ) {
768 windowX = tr.viewDef->scissor.x1;
769 }
770 if ( windowY > tr.viewDef->scissor.y2 ) {
771 windowY = tr.viewDef->scissor.y2;
772 } else if ( windowY < tr.viewDef->scissor.y1 ) {
773 windowY = tr.viewDef->scissor.y1;
774 }
775
776 r.AddPoint( windowX, windowY );
777 }
778 }
779
780 // add the fudge boundary
781 r.Expand();
782
783 return r;
784 }
785
786 /*
787 ==================
788 R_CalcLightScissorRectangle
789
790 The light screen bounds will be used to crop the scissor rect during
791 stencil clears and interaction drawing
792 ==================
793 */
794 int c_clippedLight, c_unclippedLight;
795
R_CalcLightScissorRectangle(viewLight_t * vLight)796 idScreenRect R_CalcLightScissorRectangle( viewLight_t *vLight ) {
797 idScreenRect r;
798 srfTriangles_t *tri;
799 idPlane eye, clip;
800 idVec3 ndc;
801
802 if ( vLight->lightDef->parms.pointLight ) {
803 idBounds bounds;
804 idRenderLightLocal *lightDef = vLight->lightDef;
805 tr.viewDef->viewFrustum.ProjectionBounds( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), bounds );
806 return R_ScreenRectFromViewFrustumBounds( bounds );
807 }
808
809 if ( r_useClippedLightScissors.GetInteger() == 2 ) {
810 return R_ClippedLightScissorRectangle( vLight );
811 }
812
813 r.Clear();
814
815 tri = vLight->lightDef->frustumTris;
816 for ( int i = 0 ; i < tri->numVerts ; i++ ) {
817 R_TransformModelToClip( tri->verts[i].xyz, tr.viewDef->worldSpace.modelViewMatrix,
818 tr.viewDef->projectionMatrix, eye, clip );
819
820 // if it is near clipped, clip the winding polygons to the view frustum
821 if ( clip[3] <= 1 ) {
822 c_clippedLight++;
823 if ( r_useClippedLightScissors.GetInteger() ) {
824 return R_ClippedLightScissorRectangle( vLight );
825 } else {
826 r.x1 = r.y1 = 0;
827 r.x2 = ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) - 1;
828 r.y2 = ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) - 1;
829 return r;
830 }
831 }
832
833 R_TransformClipToDevice( clip, tr.viewDef, ndc );
834
835 float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
836 float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
837
838 if ( windowX > tr.viewDef->scissor.x2 ) {
839 windowX = tr.viewDef->scissor.x2;
840 } else if ( windowX < tr.viewDef->scissor.x1 ) {
841 windowX = tr.viewDef->scissor.x1;
842 }
843 if ( windowY > tr.viewDef->scissor.y2 ) {
844 windowY = tr.viewDef->scissor.y2;
845 } else if ( windowY < tr.viewDef->scissor.y1 ) {
846 windowY = tr.viewDef->scissor.y1;
847 }
848
849 r.AddPoint( windowX, windowY );
850 }
851
852 // add the fudge boundary
853 r.Expand();
854
855 c_unclippedLight++;
856
857 return r;
858 }
859
860 /*
861 =================
862 R_AddLightSurfaces
863
864 Calc the light shader values, removing any light from the viewLight list
865 if it is determined to not have any visible effect due to being flashed off or turned off.
866
867 Adds entities to the viewEntity list if they are needed for shadow casting.
868
869 Add any precomputed shadow volumes.
870
871 Removes lights from the viewLights list if they are completely
872 turned off, or completely off screen.
873
874 Create any new interactions needed between the viewLights
875 and the viewEntitys due to game movement
876 =================
877 */
R_AddLightSurfaces(void)878 void R_AddLightSurfaces( void ) {
879 viewLight_t *vLight;
880 idRenderLightLocal *light;
881 viewLight_t **ptr;
882
883 // go through each visible light, possibly removing some from the list
884 ptr = &tr.viewDef->viewLights;
885 while ( *ptr ) {
886 vLight = *ptr;
887 light = vLight->lightDef;
888
889 const idMaterial *lightShader = light->lightShader;
890 if ( !lightShader ) {
891 common->Error( "R_AddLightSurfaces: NULL lightShader" );
892 }
893
894 // see if we are suppressing the light in this view
895 if ( !r_skipSuppress.GetBool() ) {
896 if ( light->parms.suppressLightInViewID
897 && light->parms.suppressLightInViewID == tr.viewDef->renderView.viewID ) {
898 *ptr = vLight->next;
899 light->viewCount = -1;
900 continue;
901 }
902 if ( light->parms.allowLightInViewID
903 && light->parms.allowLightInViewID != tr.viewDef->renderView.viewID ) {
904 *ptr = vLight->next;
905 light->viewCount = -1;
906 continue;
907 }
908 }
909
910 // evaluate the light shader registers
911 float *lightRegs =(float *)R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ) );
912 vLight->shaderRegisters = lightRegs;
913 lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, tr.viewDef, light->parms.referenceSound );
914
915 // if this is a purely additive light and no stage in the light shader evaluates
916 // to a positive light value, we can completely skip the light
917 if ( !lightShader->IsFogLight() && !lightShader->IsBlendLight() ) {
918 int lightStageNum;
919 for ( lightStageNum = 0 ; lightStageNum < lightShader->GetNumStages() ; lightStageNum++ ) {
920 const shaderStage_t *lightStage = lightShader->GetStage( lightStageNum );
921
922 // ignore stages that fail the condition
923 if ( !lightRegs[ lightStage->conditionRegister ] ) {
924 continue;
925 }
926
927 const int *registers = lightStage->color.registers;
928
929 // snap tiny values to zero to avoid lights showing up with the wrong color
930 if ( lightRegs[ registers[0] ] < 0.001f ) {
931 lightRegs[ registers[0] ] = 0.0f;
932 }
933 if ( lightRegs[ registers[1] ] < 0.001f ) {
934 lightRegs[ registers[1] ] = 0.0f;
935 }
936 if ( lightRegs[ registers[2] ] < 0.001f ) {
937 lightRegs[ registers[2] ] = 0.0f;
938 }
939
940 // FIXME: when using the following values the light shows up bright red when using nvidia drivers/hardware
941 // this seems to have been fixed ?
942 //lightRegs[ registers[0] ] = 1.5143074e-005f;
943 //lightRegs[ registers[1] ] = 1.5483369e-005f;
944 //lightRegs[ registers[2] ] = 1.7014690e-005f;
945
946 if ( lightRegs[ registers[0] ] > 0.0f ||
947 lightRegs[ registers[1] ] > 0.0f ||
948 lightRegs[ registers[2] ] > 0.0f ) {
949 break;
950 }
951 }
952 if ( lightStageNum == lightShader->GetNumStages() ) {
953 // we went through all the stages and didn't find one that adds anything
954 // remove the light from the viewLights list, and change its frame marker
955 // so interaction generation doesn't think the light is visible and
956 // create a shadow for it
957 *ptr = vLight->next;
958 light->viewCount = -1;
959 continue;
960 }
961 }
962
963 if ( r_useLightScissors.GetBool() ) {
964 // calculate the screen area covered by the light frustum
965 // which will be used to crop the stencil cull
966 idScreenRect scissorRect = R_CalcLightScissorRectangle( vLight );
967 // intersect with the portal crossing scissor rectangle
968 vLight->scissorRect.Intersect( scissorRect );
969
970 if ( r_showLightScissors.GetBool() ) {
971 R_ShowColoredScreenRect( vLight->scissorRect, light->index );
972 }
973 }
974
975 #if 0
976 // this never happens, because CullLightByPortals() does a more precise job
977 if ( vLight->scissorRect.IsEmpty() ) {
978 // this light doesn't touch anything on screen, so remove it from the list
979 *ptr = vLight->next;
980 continue;
981 }
982 #endif
983
984 // this one stays on the list
985 ptr = &vLight->next;
986
987 // if we are doing a soft-shadow novelty test, regenerate the light with
988 // a random offset every time
989 if ( r_lightSourceRadius.GetFloat() != 0.0f ) {
990 for ( int i = 0 ; i < 3 ; i++ ) {
991 light->globalLightOrigin[i] += r_lightSourceRadius.GetFloat() * ( -1 + 2 * (rand()&0xfff)/(float)0xfff );
992 }
993 }
994
995 // create interactions with all entities the light may touch, and add viewEntities
996 // that may cast shadows, even if they aren't directly visible. Any real work
997 // will be deferred until we walk through the viewEntities
998 tr.viewDef->renderWorld->CreateLightDefInteractions( light );
999 tr.pc.c_viewLights++;
1000
1001 // fog lights will need to draw the light frustum triangles, so make sure they
1002 // are in the vertex cache
1003 if ( lightShader->IsFogLight() ) {
1004 if ( !light->frustumTris->ambientCache ) {
1005 if ( !R_CreateAmbientCache( light->frustumTris, false ) ) {
1006 // skip if we are out of vertex memory
1007 continue;
1008 }
1009 }
1010 // touch the surface so it won't get purged
1011 vertexCache.Touch( light->frustumTris->ambientCache );
1012 }
1013
1014 // add the prelight shadows for the static world geometry
1015 if ( light->parms.prelightModel && r_useOptimizedShadows.GetBool() ) {
1016
1017 if ( !light->parms.prelightModel->NumSurfaces() ) {
1018 common->Error( "no surfs in prelight model '%s'", light->parms.prelightModel->Name() );
1019 }
1020
1021 srfTriangles_t *tri = light->parms.prelightModel->Surface( 0 )->geometry;
1022 if ( !tri->shadowVertexes ) {
1023 common->Error( "R_AddLightSurfaces: prelight model '%s' without shadowVertexes", light->parms.prelightModel->Name() );
1024 }
1025
1026 // these shadows will all have valid bounds, and can be culled normally
1027 if ( r_useShadowCulling.GetBool() ) {
1028 if ( R_CullLocalBox( tri->bounds, tr.viewDef->worldSpace.modelMatrix, 5, tr.viewDef->frustum ) ) {
1029 continue;
1030 }
1031 }
1032
1033 // if we have been purged, re-upload the shadowVertexes
1034 if ( !tri->shadowCache ) {
1035 R_CreatePrivateShadowCache( tri );
1036 if ( !tri->shadowCache ) {
1037 continue;
1038 }
1039 }
1040
1041 // touch the shadow surface so it won't get purged
1042 vertexCache.Touch( tri->shadowCache );
1043
1044 if ( !tri->indexCache && r_useIndexBuffers.GetBool() ) {
1045 vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
1046 }
1047 if ( tri->indexCache ) {
1048 vertexCache.Touch( tri->indexCache );
1049 }
1050
1051 R_LinkLightSurf( &vLight->globalShadows, tri, NULL, light, NULL, vLight->scissorRect, true /* FIXME? */ );
1052 }
1053 }
1054 }
1055
1056 //===============================================================================================================
1057
1058 /*
1059 ==================
1060 R_IssueEntityDefCallback
1061 ==================
1062 */
R_IssueEntityDefCallback(idRenderEntityLocal * def)1063 bool R_IssueEntityDefCallback( idRenderEntityLocal *def ) {
1064 bool update;
1065 idBounds oldBounds;
1066 const bool checkBounds = r_checkBounds.GetBool();
1067
1068 if ( checkBounds ) {
1069 oldBounds = def->referenceBounds;
1070 }
1071
1072 def->archived = false; // will need to be written to the demo file
1073 tr.pc.c_entityDefCallbacks++;
1074 if ( tr.viewDef ) {
1075 update = def->parms.callback( &def->parms, &tr.viewDef->renderView );
1076 } else {
1077 update = def->parms.callback( &def->parms, NULL );
1078 }
1079
1080 if ( !def->parms.hModel ) {
1081 common->Error( "R_IssueEntityDefCallback: dynamic entity callback didn't set model" );
1082 return false;
1083 }
1084
1085 if ( checkBounds ) {
1086 if ( oldBounds[0][0] > def->referenceBounds[0][0] + CHECK_BOUNDS_EPSILON ||
1087 oldBounds[0][1] > def->referenceBounds[0][1] + CHECK_BOUNDS_EPSILON ||
1088 oldBounds[0][2] > def->referenceBounds[0][2] + CHECK_BOUNDS_EPSILON ||
1089 oldBounds[1][0] < def->referenceBounds[1][0] - CHECK_BOUNDS_EPSILON ||
1090 oldBounds[1][1] < def->referenceBounds[1][1] - CHECK_BOUNDS_EPSILON ||
1091 oldBounds[1][2] < def->referenceBounds[1][2] - CHECK_BOUNDS_EPSILON ) {
1092 common->Printf( "entity %i callback extended reference bounds\n", def->index );
1093 }
1094 }
1095
1096 return update;
1097 }
1098
1099 /*
1100 ===================
1101 R_EntityDefDynamicModel
1102
1103 Issues a deferred entity callback if necessary.
1104 If the model isn't dynamic, it returns the original.
1105 Returns the cached dynamic model if present, otherwise creates
1106 it and any necessary overlays
1107 ===================
1108 */
R_EntityDefDynamicModel(idRenderEntityLocal * def)1109 idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def ) {
1110 bool callbackUpdate;
1111
1112 // allow deferred entities to construct themselves
1113 if ( def->parms.callback ) {
1114 callbackUpdate = R_IssueEntityDefCallback( def );
1115 } else {
1116 callbackUpdate = false;
1117 }
1118
1119 idRenderModel *model = def->parms.hModel;
1120
1121 if ( !model ) {
1122 common->Error( "R_EntityDefDynamicModel: NULL model" );
1123 }
1124
1125 if ( model->IsDynamicModel() == DM_STATIC ) {
1126 def->dynamicModel = NULL;
1127 def->dynamicModelFrameCount = 0;
1128 return model;
1129 }
1130
1131 // continously animating models (particle systems, etc) will have their snapshot updated every single view
1132 if ( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) {
1133 R_ClearEntityDefDynamicModel( def );
1134 }
1135
1136 // if we don't have a snapshot of the dynamic model, generate it now
1137 if ( !def->dynamicModel ) {
1138
1139 // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot
1140 def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel );
1141
1142 if ( def->cachedDynamicModel ) {
1143
1144 // add any overlays to the snapshot of the dynamic model
1145 if ( def->overlay && !r_skipOverlays.GetBool() ) {
1146 def->overlay->AddOverlaySurfacesToModel( def->cachedDynamicModel );
1147 } else {
1148 idRenderModelOverlay::RemoveOverlaySurfacesFromModel( def->cachedDynamicModel );
1149 }
1150
1151 if ( r_checkBounds.GetBool() ) {
1152 idBounds b = def->cachedDynamicModel->Bounds();
1153 if ( b[0][0] < def->referenceBounds[0][0] - CHECK_BOUNDS_EPSILON ||
1154 b[0][1] < def->referenceBounds[0][1] - CHECK_BOUNDS_EPSILON ||
1155 b[0][2] < def->referenceBounds[0][2] - CHECK_BOUNDS_EPSILON ||
1156 b[1][0] > def->referenceBounds[1][0] + CHECK_BOUNDS_EPSILON ||
1157 b[1][1] > def->referenceBounds[1][1] + CHECK_BOUNDS_EPSILON ||
1158 b[1][2] > def->referenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) {
1159 common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index );
1160 }
1161 }
1162 }
1163
1164 def->dynamicModel = def->cachedDynamicModel;
1165 def->dynamicModelFrameCount = tr.frameCount;
1166 }
1167
1168 // set model depth hack value
1169 if ( def->dynamicModel && model->DepthHack() != 0.0f && tr.viewDef ) {
1170 idPlane eye, clip;
1171 idVec3 ndc;
1172 R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
1173 R_TransformClipToDevice( clip, tr.viewDef, ndc );
1174 def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z );
1175 }
1176
1177 // FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the
1178 // undeformed surfaces. This would allow deforms to be light interacting.
1179
1180 return def->dynamicModel;
1181 }
1182
1183 /*
1184 =================
1185 R_AddDrawSurf
1186 =================
1187 */
R_AddDrawSurf(const srfTriangles_t * tri,const viewEntity_t * space,const renderEntity_t * renderEntity,const idMaterial * shader,const idScreenRect & scissor)1188 void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
1189 const idMaterial *shader, const idScreenRect &scissor ) {
1190 drawSurf_t *drawSurf;
1191 const float *shaderParms;
1192 static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch
1193 float generatedShaderParms[MAX_ENTITY_SHADER_PARMS];
1194
1195 drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
1196 drawSurf->geo = tri;
1197 drawSurf->space = space;
1198 drawSurf->material = shader;
1199 drawSurf->scissorRect = scissor;
1200 drawSurf->sort = shader->GetSort() + tr.sortOffset;
1201 drawSurf->dsFlags = 0;
1202
1203 // bumping this offset each time causes surfaces with equal sort orders to still
1204 // deterministically draw in the order they are added
1205 tr.sortOffset += 0.000001f;
1206
1207 // if it doesn't fit, resize the list
1208 if ( tr.viewDef->numDrawSurfs == tr.viewDef->maxDrawSurfs ) {
1209 drawSurf_t **old = tr.viewDef->drawSurfs;
1210 int count;
1211
1212 if ( tr.viewDef->maxDrawSurfs == 0 ) {
1213 tr.viewDef->maxDrawSurfs = INITIAL_DRAWSURFS;
1214 count = 0;
1215 } else {
1216 count = tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] );
1217 tr.viewDef->maxDrawSurfs *= 2;
1218 }
1219 tr.viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) );
1220 memcpy( tr.viewDef->drawSurfs, old, count );
1221 }
1222 tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf;
1223 tr.viewDef->numDrawSurfs++;
1224
1225 // process the shader expressions for conditionals / color / texcoords
1226 const float *constRegs = shader->ConstantRegisters();
1227 if ( constRegs ) {
1228 // shader only uses constant values
1229 drawSurf->shaderRegisters = constRegs;
1230 } else {
1231 float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
1232 drawSurf->shaderRegisters = regs;
1233
1234 // a reference shader will take the calculated stage color value from another shader
1235 // and use that for the parm0-parm3 of the current shader, which allows a stage of
1236 // a light model and light flares to pick up different flashing tables from
1237 // different light shaders
1238 if ( renderEntity->referenceShader ) {
1239 // evaluate the reference shader to find our shader parms
1240 const shaderStage_t *pStage;
1241
1242 renderEntity->referenceShader->EvaluateRegisters( refRegs, renderEntity->shaderParms, tr.viewDef, renderEntity->referenceSound );
1243 pStage = renderEntity->referenceShader->GetStage(0);
1244
1245 memcpy( generatedShaderParms, renderEntity->shaderParms, sizeof( generatedShaderParms ) );
1246 generatedShaderParms[0] = refRegs[ pStage->color.registers[0] ];
1247 generatedShaderParms[1] = refRegs[ pStage->color.registers[1] ];
1248 generatedShaderParms[2] = refRegs[ pStage->color.registers[2] ];
1249
1250 shaderParms = generatedShaderParms;
1251 } else {
1252 // evaluate with the entityDef's shader parms
1253 shaderParms = renderEntity->shaderParms;
1254 }
1255
1256 float oldFloatTime = 0.0f;
1257 int oldTime = 0;
1258
1259 if ( space->entityDef && space->entityDef->parms.timeGroup ) {
1260 oldFloatTime = tr.viewDef->floatTime;
1261 oldTime = tr.viewDef->renderView.time;
1262
1263 tr.viewDef->floatTime = game->GetTimeGroupTime( space->entityDef->parms.timeGroup ) * 0.001;
1264 tr.viewDef->renderView.time = game->GetTimeGroupTime( space->entityDef->parms.timeGroup );
1265 }
1266
1267 shader->EvaluateRegisters( regs, shaderParms, tr.viewDef, renderEntity->referenceSound );
1268
1269 if ( space->entityDef && space->entityDef->parms.timeGroup ) {
1270 tr.viewDef->floatTime = oldFloatTime;
1271 tr.viewDef->renderView.time = oldTime;
1272 }
1273 }
1274
1275 // check for deformations
1276 R_DeformDrawSurf( drawSurf );
1277
1278 // skybox surfaces need a dynamic texgen
1279 switch( shader->Texgen() ) {
1280 case TG_SKYBOX_CUBE:
1281 R_SkyboxTexGen( drawSurf, tr.viewDef->renderView.vieworg );
1282 break;
1283 case TG_WOBBLESKY_CUBE:
1284 R_WobbleskyTexGen( drawSurf, tr.viewDef->renderView.vieworg );
1285 break;
1286 }
1287
1288 // check for gui surfaces
1289 idUserInterface *gui = NULL;
1290
1291 if ( !space->entityDef ) {
1292 gui = shader->GlobalGui();
1293 } else {
1294 int guiNum = shader->GetEntityGui() - 1;
1295 if ( guiNum >= 0 && guiNum < MAX_RENDERENTITY_GUI ) {
1296 gui = renderEntity->gui[ guiNum ];
1297 }
1298 if ( gui == NULL ) {
1299 gui = shader->GlobalGui();
1300 }
1301 }
1302
1303 if ( gui ) {
1304 // force guis on the fast time
1305 float oldFloatTime;
1306 int oldTime;
1307
1308 oldFloatTime = tr.viewDef->floatTime;
1309 oldTime = tr.viewDef->renderView.time;
1310
1311 tr.viewDef->floatTime = game->GetTimeGroupTime( 1 ) * 0.001;
1312 tr.viewDef->renderView.time = game->GetTimeGroupTime( 1 );
1313
1314 idBounds ndcBounds;
1315
1316 if ( !R_PreciseCullSurface( drawSurf, ndcBounds ) ) {
1317 // did we ever use this to forward an entity color to a gui that didn't set color?
1318 // memcpy( tr.guiShaderParms, shaderParms, sizeof( tr.guiShaderParms ) );
1319 R_RenderGuiSurf( gui, drawSurf );
1320 }
1321
1322 tr.viewDef->floatTime = oldFloatTime;
1323 tr.viewDef->renderView.time = oldTime;
1324 }
1325
1326 // we can't add subviews at this point, because that would
1327 // increment tr.viewCount, messing up the rest of the surface
1328 // adds for this view
1329 }
1330
1331 /*
1332 ===============
1333 R_AddAmbientDrawsurfs
1334
1335 Adds surfaces for the given viewEntity
1336 Walks through the viewEntitys list and creates drawSurf_t for each surface of
1337 each viewEntity that has a non-empty scissorRect
1338 ===============
1339 */
R_AddAmbientDrawsurfs(viewEntity_t * vEntity)1340 static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
1341 int i, total;
1342 idRenderEntityLocal *def;
1343 srfTriangles_t *tri;
1344 idRenderModel *model;
1345 const idMaterial *shader;
1346
1347 def = vEntity->entityDef;
1348
1349 if ( def->dynamicModel ) {
1350 model = def->dynamicModel;
1351 } else {
1352 model = def->parms.hModel;
1353 }
1354
1355 // add all the surfaces
1356 total = model->NumSurfaces();
1357 for ( i = 0 ; i < total ; i++ ) {
1358 const modelSurface_t *surf = model->Surface( i );
1359
1360 // for debugging, only show a single surface at a time
1361 if ( r_singleSurface.GetInteger() >= 0 && i != r_singleSurface.GetInteger() ) {
1362 continue;
1363 }
1364
1365 tri = surf->geometry;
1366 if ( !tri ) {
1367 continue;
1368 }
1369 if ( !tri->numIndexes ) {
1370 continue;
1371 }
1372 shader = surf->shader;
1373 shader = R_RemapShaderBySkin( shader, def->parms.customSkin, def->parms.customShader );
1374
1375 R_GlobalShaderOverride( &shader );
1376
1377 if ( !shader ) {
1378 continue;
1379 }
1380 if ( !shader->IsDrawn() ) {
1381 continue;
1382 }
1383
1384 // debugging tool to make sure we are have the correct pre-calculated bounds
1385 if ( r_checkBounds.GetBool() ) {
1386 int j, k;
1387 for ( j = 0 ; j < tri->numVerts ; j++ ) {
1388 for ( k = 0 ; k < 3 ; k++ ) {
1389 if ( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON
1390 || tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON ) {
1391 common->Printf( "bad tri->bounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
1392 break;
1393 }
1394 if ( tri->verts[j].xyz[k] > def->referenceBounds[1][k] + CHECK_BOUNDS_EPSILON
1395 || tri->verts[j].xyz[k] < def->referenceBounds[0][k] - CHECK_BOUNDS_EPSILON ) {
1396 common->Printf( "bad referenceBounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
1397 break;
1398 }
1399 }
1400 if ( k != 3 ) {
1401 break;
1402 }
1403 }
1404 }
1405
1406 if ( !R_CullLocalBox( tri->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
1407
1408 def->visibleCount = tr.viewCount;
1409
1410 // make sure we have an ambient cache
1411 if ( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) {
1412 // don't add anything if the vertex cache was too full to give us an ambient cache
1413 return;
1414 }
1415 // touch it so it won't get purged
1416 vertexCache.Touch( tri->ambientCache );
1417
1418 if ( r_useIndexBuffers.GetBool() && !tri->indexCache ) {
1419 vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
1420 }
1421 if ( tri->indexCache ) {
1422 vertexCache.Touch( tri->indexCache );
1423 }
1424
1425 // add the surface for drawing
1426 R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect );
1427
1428 // ambientViewCount is used to allow light interactions to be rejected
1429 // if the ambient surface isn't visible at all
1430 tri->ambientViewCount = tr.viewCount;
1431 }
1432 }
1433
1434 // add the lightweight decal surfaces
1435 for ( idRenderModelDecal *decal = def->decals; decal; decal = decal->Next() ) {
1436 decal->AddDecalDrawSurf( vEntity );
1437 }
1438 }
1439
1440 /*
1441 ==================
1442 R_CalcEntityScissorRectangle
1443 ==================
1444 */
R_CalcEntityScissorRectangle(viewEntity_t * vEntity)1445 idScreenRect R_CalcEntityScissorRectangle( viewEntity_t *vEntity ) {
1446 idBounds bounds;
1447 idRenderEntityLocal *def = vEntity->entityDef;
1448
1449 tr.viewDef->viewFrustum.ProjectionBounds( idBox( def->referenceBounds, def->parms.origin, def->parms.axis ), bounds );
1450
1451 return R_ScreenRectFromViewFrustumBounds( bounds );
1452 }
1453
1454 /*
1455 ===================
1456 R_AddModelSurfaces
1457
1458 Here is where dynamic models actually get instantiated, and necessary
1459 interactions get created. This is all done on a sort-by-model basis
1460 to keep source data in cache (most likely L2) as any interactions and
1461 shadows are generated, since dynamic models will typically be lit by
1462 two or more lights.
1463 ===================
1464 */
R_AddModelSurfaces(void)1465 void R_AddModelSurfaces( void ) {
1466 viewEntity_t *vEntity;
1467 idInteraction *inter, *next;
1468 idRenderModel *model;
1469
1470 // clear the ambient surface list
1471 tr.viewDef->numDrawSurfs = 0;
1472 tr.viewDef->maxDrawSurfs = 0; // will be set to INITIAL_DRAWSURFS on R_AddDrawSurf
1473
1474 // go through each entity that is either visible to the view, or to
1475 // any light that intersects the view (for shadows)
1476 for ( vEntity = tr.viewDef->viewEntitys; vEntity; vEntity = vEntity->next ) {
1477
1478 if ( r_useEntityScissors.GetBool() ) {
1479 // calculate the screen area covered by the entity
1480 idScreenRect scissorRect = R_CalcEntityScissorRectangle( vEntity );
1481 // intersect with the portal crossing scissor rectangle
1482 vEntity->scissorRect.Intersect( scissorRect );
1483
1484 if ( r_showEntityScissors.GetBool() ) {
1485 R_ShowColoredScreenRect( vEntity->scissorRect, vEntity->entityDef->index );
1486 }
1487 }
1488
1489 float oldFloatTime = 0.0f;
1490 int oldTime = 0;
1491
1492 game->SelectTimeGroup( vEntity->entityDef->parms.timeGroup );
1493
1494 if ( vEntity->entityDef->parms.timeGroup ) {
1495 oldFloatTime = tr.viewDef->floatTime;
1496 oldTime = tr.viewDef->renderView.time;
1497
1498 tr.viewDef->floatTime = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup ) * 0.001;
1499 tr.viewDef->renderView.time = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup );
1500 }
1501
1502 if ( tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 1 ) {
1503 if ( vEntity->entityDef->parms.timeGroup ) {
1504 tr.viewDef->floatTime = oldFloatTime;
1505 tr.viewDef->renderView.time = oldTime;
1506 }
1507 continue;
1508 } else if ( !tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 2 ) {
1509 if ( vEntity->entityDef->parms.timeGroup ) {
1510 tr.viewDef->floatTime = oldFloatTime;
1511 tr.viewDef->renderView.time = oldTime;
1512 }
1513 continue;
1514 }
1515
1516 // add the ambient surface if it has a visible rectangle
1517 if ( !vEntity->scissorRect.IsEmpty() ) {
1518 model = R_EntityDefDynamicModel( vEntity->entityDef );
1519 if ( model == NULL || model->NumSurfaces() <= 0 ) {
1520 if ( vEntity->entityDef->parms.timeGroup ) {
1521 tr.viewDef->floatTime = oldFloatTime;
1522 tr.viewDef->renderView.time = oldTime;
1523 }
1524 continue;
1525 }
1526
1527 R_AddAmbientDrawsurfs( vEntity );
1528 tr.pc.c_visibleViewEntities++;
1529 } else {
1530 tr.pc.c_shadowViewEntities++;
1531 }
1532
1533 //
1534 // for all the entity / light interactions on this entity, add them to the view
1535 //
1536 if ( tr.viewDef->isXraySubview ) {
1537 if ( vEntity->entityDef->parms.xrayIndex == 2 ) {
1538 for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
1539 next = inter->entityNext;
1540 if ( inter->lightDef->viewCount != tr.viewCount ) {
1541 continue;
1542 }
1543 inter->AddActiveInteraction();
1544 }
1545 }
1546 } else {
1547 // all empty interactions are at the end of the list so once the
1548 // first is encountered all the remaining interactions are empty
1549 for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
1550 next = inter->entityNext;
1551
1552 // skip any lights that aren't currently visible
1553 // this is run after any lights that are turned off have already
1554 // been removed from the viewLights list, and had their viewCount cleared
1555 if ( inter->lightDef->viewCount != tr.viewCount ) {
1556 continue;
1557 }
1558 inter->AddActiveInteraction();
1559 }
1560 }
1561
1562 if ( vEntity->entityDef->parms.timeGroup ) {
1563 tr.viewDef->floatTime = oldFloatTime;
1564 tr.viewDef->renderView.time = oldTime;
1565 }
1566
1567 }
1568 }
1569
1570 /*
1571 =====================
1572 R_RemoveUnecessaryViewLights
1573 =====================
1574 */
R_RemoveUnecessaryViewLights(void)1575 void R_RemoveUnecessaryViewLights( void ) {
1576 viewLight_t *vLight;
1577
1578 // go through each visible light
1579 for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
1580 // if the light didn't have any lit surfaces visible, there is no need to
1581 // draw any of the shadows. We still keep the vLight for debugging
1582 // draws
1583 if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) {
1584 vLight->localShadows = NULL;
1585 vLight->globalShadows = NULL;
1586 }
1587 }
1588
1589 if ( r_useShadowSurfaceScissor.GetBool() ) {
1590 // shrink the light scissor rect to only intersect the surfaces that will actually be drawn.
1591 // This doesn't seem to actually help, perhaps because the surface scissor
1592 // rects aren't actually the surface, but only the portal clippings.
1593 for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
1594 const drawSurf_t *surf;
1595 idScreenRect surfRect;
1596
1597 if ( !vLight->lightShader->LightCastsShadows() ) {
1598 continue;
1599 }
1600
1601 surfRect.Clear();
1602
1603 for ( surf = vLight->globalInteractions ; surf ; surf = surf->nextOnLight ) {
1604 surfRect.Union( surf->scissorRect );
1605 }
1606 for ( surf = vLight->localShadows ; surf ; surf = surf->nextOnLight ) {
1607 const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
1608 }
1609
1610 for ( surf = vLight->localInteractions ; surf ; surf = surf->nextOnLight ) {
1611 surfRect.Union( surf->scissorRect );
1612 }
1613 for ( surf = vLight->globalShadows ; surf ; surf = surf->nextOnLight ) {
1614 const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
1615 }
1616
1617 for ( surf = vLight->translucentInteractions ; surf ; surf = surf->nextOnLight ) {
1618 surfRect.Union( surf->scissorRect );
1619 }
1620
1621 vLight->scissorRect.Intersect( surfRect );
1622 }
1623 }
1624 }
1625