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