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 }