DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

tr_frontend_subview.cpp (16167B)


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