]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/tr_subview.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / renderer / tr_subview.cpp
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 "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33
34
35 typedef struct {
36         idVec3          origin;
37         idMat3          axis;
38 } orientation_t;
39
40
41 /*
42 =================
43 R_MirrorPoint
44 =================
45 */
46 static void R_MirrorPoint( const idVec3 in, orientation_t *surface, orientation_t *camera, idVec3 &out ) {
47         int             i;
48         idVec3  local;
49         idVec3  transformed;
50         float   d;
51
52         local = in - surface->origin;
53
54         transformed = vec3_origin;
55         for ( i = 0 ; i < 3 ; i++ ) {
56                 d = local * surface->axis[i];
57                 transformed += d * camera->axis[i];
58         }
59
60         out = transformed + camera->origin;
61 }
62
63 /*
64 =================
65 R_MirrorVector
66 =================
67 */
68 static void R_MirrorVector( const idVec3 in, orientation_t *surface, orientation_t *camera, idVec3 &out ) {
69         int             i;
70         float   d;
71
72         out = vec3_origin;
73         for ( i = 0 ; i < 3 ; i++ ) {
74                 d = in * surface->axis[i];
75                 out += d * camera->axis[i];
76         }
77 }
78
79 /*
80 =============
81 R_PlaneForSurface
82
83 Returns the plane for the first triangle in the surface
84 FIXME: check for degenerate triangle?
85 =============
86 */
87 static void R_PlaneForSurface( const srfTriangles_t *tri, idPlane &plane ) {
88         idDrawVert              *v1, *v2, *v3;
89
90         v1 = tri->verts + tri->indexes[0];
91         v2 = tri->verts + tri->indexes[1];
92         v3 = tri->verts + tri->indexes[2];
93         plane.FromPoints( v1->xyz, v2->xyz, v3->xyz );
94 }
95
96 /*
97 =========================
98 R_PreciseCullSurface
99
100 Check the surface for visibility on a per-triangle basis
101 for cases when it is going to be VERY expensive to draw (subviews)
102
103 If not culled, also returns the bounding box of the surface in 
104 Normalized Device Coordinates, so it can be used to crop the scissor rect.
105
106 OPTIMIZE: we could also take exact portal passing into consideration
107 =========================
108 */
109 bool R_PreciseCullSurface( const drawSurf_t *drawSurf, idBounds &ndcBounds ) {
110         const srfTriangles_t *tri;
111         int numTriangles;
112         idPlane clip, eye;
113         int i, j;
114         unsigned int pointOr;
115         unsigned int pointAnd;
116         idVec3 localView;
117         idFixedWinding w;
118
119         tri = drawSurf->geo;
120
121         pointOr = 0;
122         pointAnd = (unsigned int)~0;
123
124         // get an exact bounds of the triangles for scissor cropping
125         ndcBounds.Clear();
126
127         for ( i = 0; i < tri->numVerts; i++ ) {
128                 int j;
129                 unsigned int pointFlags;
130
131                 R_TransformModelToClip( tri->verts[i].xyz, drawSurf->space->modelViewMatrix,
132                         tr.viewDef->projectionMatrix, eye, clip );
133
134                 pointFlags = 0;
135                 for ( j = 0; j < 3; j++ ) {
136                         if ( clip[j] >= clip[3] ) {
137                                 pointFlags |= (1 << (j*2));
138                         } else if ( clip[j] <= -clip[3] ) {
139                                 pointFlags |= ( 1 << (j*2+1));
140                         }
141                 }
142
143                 pointAnd &= pointFlags;
144                 pointOr |= pointFlags;
145         }
146
147         // trivially reject
148         if ( pointAnd ) {
149                 return true;
150         }
151
152         // backface and frustum cull
153         numTriangles = tri->numIndexes / 3;
154
155         R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView );
156
157         for ( i = 0; i < tri->numIndexes; i += 3 ) {
158                 idVec3  dir, normal;
159                 float   dot;
160                 idVec3  d1, d2;
161
162                 const idVec3 &v1 = tri->verts[tri->indexes[i]].xyz;
163                 const idVec3 &v2 = tri->verts[tri->indexes[i+1]].xyz;
164                 const idVec3 &v3 = tri->verts[tri->indexes[i+2]].xyz;
165
166                 // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized
167                 // axis that we get from the gui view transform.  It doesn't hurt anything, because
168                 // we know that all gui generated surfaces are front facing
169                 if ( tr.guiRecursionLevel == 0 ) {
170                         // we don't care that it isn't normalized,
171                         // all we want is the sign
172                         d1 = v2 - v1;
173                         d2 = v3 - v1;
174                         normal = d2.Cross( d1 );
175
176                         dir = v1 - localView;
177
178                         dot = normal * dir;
179                         if ( dot >= 0.0f ) {
180                                 return true;
181                         }
182                 }
183
184                 // now find the exact screen bounds of the clipped triangle
185                 w.SetNumPoints( 3 );
186                 R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() );
187                 R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() );
188                 R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() );
189                 w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f;
190
191                 for ( j = 0; j < 4; j++ ) {
192                         if ( !w.ClipInPlace( -tr.viewDef->frustum[j], 0.1f ) ) {
193                                 break;
194                         }
195                 }
196                 for ( j = 0; j < w.GetNumPoints(); j++ ) {
197                         idVec3  screen;
198
199                         R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen );
200                         ndcBounds.AddPoint( screen );
201                 }
202         }
203
204         // if we don't enclose any area, return
205         if ( ndcBounds.IsCleared() ) {
206                 return true;
207         }
208
209         return false;
210 }
211
212 /*
213 ========================
214 R_MirrorViewBySurface
215 ========================
216 */
217 static viewDef_t *R_MirrorViewBySurface( drawSurf_t *drawSurf ) {
218         viewDef_t               *parms;
219         orientation_t   surface, camera;
220         idPlane                 originalPlane, plane;
221
222         // copy the viewport size from the original
223         parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
224         *parms = *tr.viewDef;
225         parms->renderView.viewID = 0;   // clear to allow player bodies to show up, and suppress view weapons
226
227         parms->isSubview = true;
228         parms->isMirror = true;
229
230         // create plane axis for the portal we are seeing
231         R_PlaneForSurface( drawSurf->geo, originalPlane );
232         R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane );
233
234         surface.origin = plane.Normal() * -plane[3];
235         surface.axis[0] = plane.Normal();
236         surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] );
237         surface.axis[2] = -surface.axis[2];
238
239         camera.origin = surface.origin;
240         camera.axis[0] = -surface.axis[0];
241         camera.axis[1] = surface.axis[1];
242         camera.axis[2] = surface.axis[2];
243
244         // set the mirrored origin and axis
245         R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg );
246
247         R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] );
248         R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] );
249         R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] );
250
251         // make the view origin 16 units away from the center of the surface
252         idVec3  viewOrigin = ( drawSurf->geo->bounds[0] + drawSurf->geo->bounds[1] ) * 0.5;
253         viewOrigin += ( originalPlane.Normal() * 16 );
254
255         R_LocalPointToGlobal( drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin );
256
257         // set the mirror clip plane
258         parms->numClipPlanes = 1;
259         parms->clipPlanes[0] = -camera.axis[0];
260
261         parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() );
262         
263         return parms;
264 }
265
266 /*
267 ========================
268 R_XrayViewBySurface
269 ========================
270 */
271 static viewDef_t *R_XrayViewBySurface( drawSurf_t *drawSurf ) {
272         viewDef_t               *parms;
273         orientation_t   surface, camera;
274         idPlane                 originalPlane, plane;
275
276         // copy the viewport size from the original
277         parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
278         *parms = *tr.viewDef;
279         parms->renderView.viewID = 0;   // clear to allow player bodies to show up, and suppress view weapons
280
281         parms->isSubview = true;
282         parms->isXraySubview = true;
283
284         return parms;
285 }
286
287 /*
288 ===============
289 R_RemoteRender
290 ===============
291 */
292 static void R_RemoteRender( drawSurf_t *surf, textureStage_t *stage ) {
293         viewDef_t               *parms;
294
295         // remote views can be reused in a single frame
296         if ( stage->dynamicFrameCount == tr.frameCount ) {
297                 return;
298         }
299
300         // if the entity doesn't have a remoteRenderView, do nothing
301         if ( !surf->space->entityDef->parms.remoteRenderView ) {
302                 return;
303         }
304
305         // copy the viewport size from the original
306         parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
307         *parms = *tr.viewDef;
308
309         parms->isSubview = true;
310         parms->isMirror = false;
311
312         parms->renderView = *surf->space->entityDef->parms.remoteRenderView;
313         parms->renderView.viewID = 0;   // clear to allow player bodies to show up, and suppress view weapons
314         parms->initialViewAreaOrigin = parms->renderView.vieworg;
315
316         tr.CropRenderSize( stage->width, stage->height, true );
317
318         parms->renderView.x = 0;
319         parms->renderView.y = 0;
320         parms->renderView.width = SCREEN_WIDTH;
321         parms->renderView.height = SCREEN_HEIGHT;
322
323         tr.RenderViewToViewport( &parms->renderView, &parms->viewport );
324
325         parms->scissor.x1 = 0;
326         parms->scissor.y1 = 0;
327         parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
328         parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
329
330         parms->superView = tr.viewDef;
331         parms->subviewSurface = surf;
332
333         // generate render commands for it
334         R_RenderView(parms);
335
336         // copy this rendering to the image
337         stage->dynamicFrameCount = tr.frameCount;
338         if (!stage->image) {
339                 stage->image = globalImages->scratchImage;
340         }
341
342         tr.CaptureRenderToImage( stage->image->imgName );
343         tr.UnCrop();
344 }
345
346 /*
347 =================
348 R_MirrorRender
349 =================
350 */
351 void R_MirrorRender( drawSurf_t *surf, textureStage_t *stage, idScreenRect scissor ) {
352         viewDef_t               *parms;
353
354         // remote views can be reused in a single frame
355         if ( stage->dynamicFrameCount == tr.frameCount ) {
356                 return;
357         }
358
359         // issue a new view command
360         parms = R_MirrorViewBySurface( surf );
361         if ( !parms ) {
362                 return;
363         }
364
365         tr.CropRenderSize( stage->width, stage->height, true );
366
367         parms->renderView.x = 0;
368         parms->renderView.y = 0;
369         parms->renderView.width = SCREEN_WIDTH;
370         parms->renderView.height = SCREEN_HEIGHT;
371
372         tr.RenderViewToViewport( &parms->renderView, &parms->viewport );
373
374         parms->scissor.x1 = 0;
375         parms->scissor.y1 = 0;
376         parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
377         parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
378
379         parms->superView = tr.viewDef;
380         parms->subviewSurface = surf;
381
382         // triangle culling order changes with mirroring
383         parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
384
385         // generate render commands for it
386         R_RenderView( parms );
387
388         // copy this rendering to the image
389         stage->dynamicFrameCount = tr.frameCount;
390         stage->image = globalImages->scratchImage;
391
392         tr.CaptureRenderToImage( stage->image->imgName );
393         tr.UnCrop();
394 }
395
396 /*
397 =================
398 R_XrayRender
399 =================
400 */
401 void R_XrayRender( drawSurf_t *surf, textureStage_t *stage, idScreenRect scissor ) {
402         viewDef_t               *parms;
403
404         // remote views can be reused in a single frame
405         if ( stage->dynamicFrameCount == tr.frameCount ) {
406                 return;
407         }
408
409         // issue a new view command
410         parms = R_XrayViewBySurface( surf );
411         if ( !parms ) {
412                 return;
413         }
414
415         tr.CropRenderSize( stage->width, stage->height, true );
416
417         parms->renderView.x = 0;
418         parms->renderView.y = 0;
419         parms->renderView.width = SCREEN_WIDTH;
420         parms->renderView.height = SCREEN_HEIGHT;
421
422         tr.RenderViewToViewport( &parms->renderView, &parms->viewport );
423
424         parms->scissor.x1 = 0;
425         parms->scissor.y1 = 0;
426         parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
427         parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
428
429         parms->superView = tr.viewDef;
430         parms->subviewSurface = surf;
431
432         // triangle culling order changes with mirroring
433         parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
434
435         // generate render commands for it
436         R_RenderView( parms );
437
438         // copy this rendering to the image
439         stage->dynamicFrameCount = tr.frameCount;
440         stage->image = globalImages->scratchImage2;
441
442         tr.CaptureRenderToImage( stage->image->imgName );
443         tr.UnCrop();
444 }
445
446 /*
447 ==================
448 R_GenerateSurfaceSubview
449 ==================
450 */
451 bool    R_GenerateSurfaceSubview( drawSurf_t *drawSurf ) {
452         idBounds                ndcBounds;
453         viewDef_t               *parms;
454         const idMaterial                *shader;
455
456         // for testing the performance hit
457         if ( r_skipSubviews.GetBool() ) {
458                 return false;
459         }
460
461         if ( R_PreciseCullSurface( drawSurf, ndcBounds ) ) {
462                 return false;
463         }
464
465         shader = drawSurf->material;
466
467         // never recurse through a subview surface that we are
468         // already seeing through
469         for ( parms = tr.viewDef ; parms ; parms = parms->superView ) {
470                 if ( parms->subviewSurface
471                         && parms->subviewSurface->geo == drawSurf->geo
472                         && parms->subviewSurface->space->entityDef == drawSurf->space->entityDef ) {
473                         break;
474                 }
475         }
476         if ( parms ) {
477                 return false;
478         }
479
480         // crop the scissor bounds based on the precise cull
481         idScreenRect    scissor;
482
483         idScreenRect    *v = &tr.viewDef->viewport;
484         scissor.x1 = v->x1 + (int)( (v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[0][0] + 1.0f ));
485         scissor.y1 = v->y1 + (int)( (v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[0][1] + 1.0f ));
486         scissor.x2 = v->x1 + (int)( (v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[1][0] + 1.0f ));
487         scissor.y2 = v->y1 + (int)( (v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[1][1] + 1.0f ));
488
489         // nudge a bit for safety
490         scissor.Expand();
491
492         scissor.Intersect( tr.viewDef->scissor );
493
494         if ( scissor.IsEmpty() ) {
495                 // cropped out
496                 return false;
497         }
498
499         // see what kind of subview we are making
500         if ( shader->GetSort() != SS_SUBVIEW ) {
501                 for ( int i = 0 ; i < shader->GetNumStages() ; i++ ) {
502                         const shaderStage_t     *stage = shader->GetStage( i );
503                         switch ( stage->texture.dynamic ) {
504                         case DI_REMOTE_RENDER:
505                                 R_RemoteRender( drawSurf, const_cast<textureStage_t *>(&stage->texture) );
506                                 break;
507                         case DI_MIRROR_RENDER:
508                                 R_MirrorRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor );
509                                 break;
510                         case DI_XRAY_RENDER:
511                                 R_XrayRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor );
512                                 break;
513                         }
514                 }
515                 return true;
516         }
517
518         // issue a new view command
519         parms = R_MirrorViewBySurface( drawSurf );
520         if ( !parms ) {
521                 return false;
522         }
523
524         parms->scissor = scissor;
525         parms->superView = tr.viewDef;
526         parms->subviewSurface = drawSurf;
527
528         // triangle culling order changes with mirroring
529         parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
530
531         // generate render commands for it
532         R_RenderView( parms );
533
534         return true;
535 }
536
537 /*
538 ================
539 R_GenerateSubViews
540
541 If we need to render another view to complete the current view,
542 generate it first.
543
544 It is important to do this after all drawSurfs for the current
545 view have been generated, because it may create a subview which
546 would change tr.viewCount.
547 ================
548 */
549 bool R_GenerateSubViews( void ) {
550         drawSurf_t              *drawSurf;
551         int                             i;
552         bool                    subviews;
553         const idMaterial                *shader;
554
555         // for testing the performance hit
556         if ( r_skipSubviews.GetBool() ) {
557                 return false;
558         }
559
560         subviews = false;
561
562         // scan the surfaces until we either find a subview, or determine
563         // there are no more subview surfaces.
564         for ( i = 0 ; i < tr.viewDef->numDrawSurfs ; i++ ) {
565                 drawSurf = tr.viewDef->drawSurfs[i];
566                 shader = drawSurf->material;
567
568                 if ( !shader || !shader->HasSubview() ) {
569                         continue;
570                 }
571
572                 if ( R_GenerateSurfaceSubview( drawSurf ) ) {
573                         subviews = true;
574                 }
575         }
576
577         return subviews;
578 }