123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "tr_local.h"
- #include "Model_local.h"
- /*
- ==========================================================================================
- SUBVIEW SURFACES
- ==========================================================================================
- */
- struct orientation_t {
- idVec3 origin;
- idMat3 axis;
- };
- /*
- =================
- R_MirrorPoint
- =================
- */
- static void R_MirrorPoint( const idVec3 in, orientation_t *surface, orientation_t *camera, idVec3 &out ) {
- const idVec3 local = in - surface->origin;
- idVec3 transformed( 0.0f );
- for ( int i = 0; i < 3; i++ ) {
- const float d = local * surface->axis[i];
- transformed += d * camera->axis[i];
- }
- out = transformed + camera->origin;
- }
- /*
- =================
- R_MirrorVector
- =================
- */
- static void R_MirrorVector( const idVec3 in, orientation_t *surface, orientation_t *camera, idVec3 &out ) {
- out.Zero();
- for ( int i = 0; i < 3; i++ ) {
- const float d = in * surface->axis[i];
- out += d * camera->axis[i];
- }
- }
- /*
- =============
- R_PlaneForSurface
- Returns the plane for the first triangle in the surface
- FIXME: check for degenerate triangle?
- =============
- */
- static void R_PlaneForSurface( const srfTriangles_t *tri, idPlane &plane ) {
- idDrawVert * v1 = tri->verts + tri->indexes[0];
- idDrawVert * v2 = tri->verts + tri->indexes[1];
- idDrawVert * v3 = tri->verts + tri->indexes[2];
- plane.FromPoints( v1->xyz, v2->xyz, v3->xyz );
- }
- /*
- =========================
- R_PreciseCullSurface
- Check the surface for visibility on a per-triangle basis
- for cases when it is going to be VERY expensive to draw (subviews)
- If not culled, also returns the bounding box of the surface in
- Normalized Device Coordinates, so it can be used to crop the scissor rect.
- OPTIMIZE: we could also take exact portal passing into consideration
- =========================
- */
- bool R_PreciseCullSurface( const drawSurf_t *drawSurf, idBounds &ndcBounds ) {
- const srfTriangles_t * tri = drawSurf->frontEndGeo;
- unsigned int pointOr = 0;
- unsigned int pointAnd = (unsigned int)~0;
- // get an exact bounds of the triangles for scissor cropping
- ndcBounds.Clear();
- const idJointMat * joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL;
- for ( int i = 0; i < tri->numVerts; i++ ) {
- const idVec3 vXYZ = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[i], joints );
- idPlane eye, clip;
- R_TransformModelToClip( vXYZ, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
- unsigned int pointFlags = 0;
- for ( int j = 0; j < 3; j++ ) {
- if ( clip[j] >= clip[3] ) {
- pointFlags |= ( 1 << (j*2+0) );
- } else if ( clip[j] <= -clip[3] ) { // FIXME: the D3D near clip plane is at zero instead of -1
- pointFlags |= ( 1 << (j*2+1) );
- }
- }
- pointAnd &= pointFlags;
- pointOr |= pointFlags;
- }
- // trivially reject
- if ( pointAnd != 0 ) {
- return true;
- }
- // backface and frustum cull
- idVec3 localViewOrigin;
- R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin );
- for ( int i = 0; i < tri->numIndexes; i += 3 ) {
- const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i+0 ] ], joints );
- const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i+1 ] ], joints );
- const idVec3 v3 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i+2 ] ], joints );
- // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized
- // axis that we get from the gui view transform. It doesn't hurt anything, because
- // we know that all gui generated surfaces are front facing
- if ( tr.guiRecursionLevel == 0 ) {
- // we don't care that it isn't normalized,
- // all we want is the sign
- const idVec3 d1 = v2 - v1;
- const idVec3 d2 = v3 - v1;
- const idVec3 normal = d2.Cross( d1 );
- const idVec3 dir = v1 - localViewOrigin;
- const float dot = normal * dir;
- if ( dot >= 0.0f ) {
- return true;
- }
- }
- // now find the exact screen bounds of the clipped triangle
- idFixedWinding w;
- w.SetNumPoints( 3 );
- R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() );
- R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() );
- R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() );
- w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f;
- for ( int j = 0; j < 4; j++ ) {
- if ( !w.ClipInPlace( -tr.viewDef->frustum[j], 0.1f ) ) {
- break;
- }
- }
- for ( int j = 0; j < w.GetNumPoints(); j++ ) {
- idVec3 screen;
- R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen );
- ndcBounds.AddPoint( screen );
- }
- }
- // if we don't enclose any area, return
- if ( ndcBounds.IsCleared() ) {
- return true;
- }
- return false;
- }
- /*
- ========================
- R_MirrorViewBySurface
- ========================
- */
- static viewDef_t *R_MirrorViewBySurface( const drawSurf_t *drawSurf ) {
- // copy the viewport size from the original
- viewDef_t * parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
- *parms = *tr.viewDef;
- parms->renderView.viewID = 0; // clear to allow player bodies to show up, and suppress view weapons
- parms->isSubview = true;
- parms->isMirror = true;
- // create plane axis for the portal we are seeing
- idPlane originalPlane, plane;
- R_PlaneForSurface( drawSurf->frontEndGeo, originalPlane );
- R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane );
- orientation_t surface;
- surface.origin = plane.Normal() * -plane[3];
- surface.axis[0] = plane.Normal();
- surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] );
- surface.axis[2] = -surface.axis[2];
- orientation_t camera;
- camera.origin = surface.origin;
- camera.axis[0] = -surface.axis[0];
- camera.axis[1] = surface.axis[1];
- camera.axis[2] = surface.axis[2];
- // set the mirrored origin and axis
- R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg );
- R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] );
- R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] );
- R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] );
- // make the view origin 16 units away from the center of the surface
- const idVec3 center = ( drawSurf->frontEndGeo->bounds[0] + drawSurf->frontEndGeo->bounds[1] ) * 0.5f;
- const idVec3 viewOrigin = center + ( originalPlane.Normal() * 16.0f );
- R_LocalPointToGlobal( drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin );
- // set the mirror clip plane
- parms->numClipPlanes = 1;
- parms->clipPlanes[0] = -camera.axis[0];
- parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() );
-
- return parms;
- }
- /*
- ========================
- R_XrayViewBySurface
- ========================
- */
- static viewDef_t *R_XrayViewBySurface( const drawSurf_t *drawSurf ) {
- // copy the viewport size from the original
- viewDef_t * parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
- *parms = *tr.viewDef;
- parms->renderView.viewID = 0; // clear to allow player bodies to show up, and suppress view weapons
- parms->isSubview = true;
- parms->isXraySubview = true;
- return parms;
- }
- /*
- ===============
- R_RemoteRender
- ===============
- */
- static void R_RemoteRender( const drawSurf_t *surf, textureStage_t *stage ) {
- // remote views can be reused in a single frame
- if ( stage->dynamicFrameCount == tr.frameCount ) {
- return;
- }
- // if the entity doesn't have a remoteRenderView, do nothing
- if ( !surf->space->entityDef->parms.remoteRenderView ) {
- return;
- }
- int stageWidth = stage->width;
- int stageHeight = stage->height;
- // copy the viewport size from the original
- viewDef_t * parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
- *parms = *tr.viewDef;
- parms->renderView = *surf->space->entityDef->parms.remoteRenderView;
- parms->renderView.viewID = 0; // clear to allow player bodies to show up, and suppress view weapons
- parms->initialViewAreaOrigin = parms->renderView.vieworg;
- parms->isSubview = true;
- parms->isMirror = false;
- tr.CropRenderSize( stageWidth, stageHeight );
- tr.GetCroppedViewport( &parms->viewport );
- parms->scissor.x1 = 0;
- parms->scissor.y1 = 0;
- parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
- parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
- parms->superView = tr.viewDef;
- parms->subviewSurface = surf;
- // generate render commands for it
- R_RenderView( parms );
- // copy this rendering to the image
- stage->dynamicFrameCount = tr.frameCount;
- if ( stage->image == NULL ) {
- stage->image = globalImages->scratchImage;
- }
- tr.CaptureRenderToImage( stage->image->GetName(), true );
- tr.UnCrop();
- }
- /*
- =================
- R_MirrorRender
- =================
- */
- void R_MirrorRender( const drawSurf_t *surf, textureStage_t *stage, idScreenRect scissor ) {
- // remote views can be reused in a single frame
- if ( stage->dynamicFrameCount == tr.frameCount ) {
- return;
- }
- // issue a new view command
- viewDef_t * parms = R_MirrorViewBySurface( surf );
- if ( parms == NULL ) {
- return;
- }
- tr.CropRenderSize( stage->width, stage->height );
- tr.GetCroppedViewport( &parms->viewport );
- parms->scissor.x1 = 0;
- parms->scissor.y1 = 0;
- parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
- parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
- parms->superView = tr.viewDef;
- parms->subviewSurface = surf;
- // triangle culling order changes with mirroring
- parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
- // generate render commands for it
- R_RenderView( parms );
- // copy this rendering to the image
- stage->dynamicFrameCount = tr.frameCount;
- stage->image = globalImages->scratchImage;
- tr.CaptureRenderToImage( stage->image->GetName() );
- tr.UnCrop();
- }
- /*
- =================
- R_XrayRender
- =================
- */
- void R_XrayRender( const drawSurf_t *surf, textureStage_t *stage, idScreenRect scissor ) {
- // remote views can be reused in a single frame
- if ( stage->dynamicFrameCount == tr.frameCount ) {
- return;
- }
- // issue a new view command
- viewDef_t * parms = R_XrayViewBySurface( surf );
- if ( parms == NULL ) {
- return;
- }
- int stageWidth = stage->width;
- int stageHeight = stage->height;
- tr.CropRenderSize( stageWidth, stageHeight );
- tr.GetCroppedViewport( &parms->viewport );
- parms->scissor.x1 = 0;
- parms->scissor.y1 = 0;
- parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
- parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
- parms->superView = tr.viewDef;
- parms->subviewSurface = surf;
- // triangle culling order changes with mirroring
- parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
- // generate render commands for it
- R_RenderView( parms );
- // copy this rendering to the image
- stage->dynamicFrameCount = tr.frameCount;
- stage->image = globalImages->scratchImage2;
- tr.CaptureRenderToImage( stage->image->GetName(), true );
- tr.UnCrop();
- }
- /*
- ==================
- R_GenerateSurfaceSubview
- ==================
- */
- bool R_GenerateSurfaceSubview( const drawSurf_t *drawSurf ) {
- // for testing the performance hit
- if ( r_skipSubviews.GetBool() ) {
- return false;
- }
- idBounds ndcBounds;
- if ( R_PreciseCullSurface( drawSurf, ndcBounds ) ) {
- return false;
- }
- const idMaterial * shader = drawSurf->material;
- // never recurse through a subview surface that we are
- // already seeing through
- viewDef_t * parms = NULL;
- for ( parms = tr.viewDef; parms != NULL; parms = parms->superView ) {
- if ( parms->subviewSurface != NULL
- && parms->subviewSurface->frontEndGeo == drawSurf->frontEndGeo
- && parms->subviewSurface->space->entityDef == drawSurf->space->entityDef ) {
- break;
- }
- }
- if ( parms ) {
- return false;
- }
- // crop the scissor bounds based on the precise cull
- assert( tr.viewDef != NULL );
- idScreenRect * v = &tr.viewDef->viewport;
- idScreenRect scissor;
- scissor.x1 = v->x1 + idMath::Ftoi( ( v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[0][0] + 1.0f ) );
- scissor.y1 = v->y1 + idMath::Ftoi( ( v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[0][1] + 1.0f ) );
- scissor.x2 = v->x1 + idMath::Ftoi( ( v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[1][0] + 1.0f ) );
- scissor.y2 = v->y1 + idMath::Ftoi( ( v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[1][1] + 1.0f ) );
- // nudge a bit for safety
- scissor.Expand();
- scissor.Intersect( tr.viewDef->scissor );
- if ( scissor.IsEmpty() ) {
- // cropped out
- return false;
- }
- // see what kind of subview we are making
- if ( shader->GetSort() != SS_SUBVIEW ) {
- for ( int i = 0; i < shader->GetNumStages(); i++ ) {
- const shaderStage_t *stage = shader->GetStage( i );
- switch ( stage->texture.dynamic ) {
- case DI_REMOTE_RENDER:
- R_RemoteRender( drawSurf, const_cast<textureStage_t *>(&stage->texture) );
- break;
- case DI_MIRROR_RENDER:
- R_MirrorRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor );
- break;
- case DI_XRAY_RENDER:
- R_XrayRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor );
- break;
- }
- }
- return true;
- }
- // issue a new view command
- parms = R_MirrorViewBySurface( drawSurf );
- if ( parms == NULL ) {
- return false;
- }
- parms->scissor = scissor;
- parms->superView = tr.viewDef;
- parms->subviewSurface = drawSurf;
- // triangle culling order changes with mirroring
- parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 );
- // generate render commands for it
- R_RenderView( parms );
- return true;
- }
- /*
- ================
- R_GenerateSubViews
- If we need to render another view to complete the current view,
- generate it first.
- It is important to do this after all drawSurfs for the current
- view have been generated, because it may create a subview which
- would change tr.viewCount.
- ================
- */
- bool R_GenerateSubViews( const drawSurf_t * const drawSurfs[], const int numDrawSurfs ) {
- SCOPED_PROFILE_EVENT( "R_GenerateSubViews" );
- // for testing the performance hit
- if ( r_skipSubviews.GetBool() ) {
- return false;
- }
- // scan the surfaces until we either find a subview, or determine
- // there are no more subview surfaces.
- bool subviews = false;
- for ( int i = 0; i < numDrawSurfs; i++ ) {
- const drawSurf_t * drawSurf = drawSurfs[i];
- if ( !drawSurf->material->HasSubview() ) {
- continue;
- }
- if ( R_GenerateSurfaceSubview( drawSurf ) ) {
- subviews = true;
- }
- }
- return subviews;
- }
|