diff --git a/neo/renderer/GuiModel.cpp b/neo/renderer/GuiModel.cpp index 08fdb2cf..3af7f02e 100644 --- a/neo/renderer/GuiModel.cpp +++ b/neo/renderer/GuiModel.cpp @@ -209,8 +209,14 @@ EmitToCurrentView void idGuiModel::EmitToCurrentView( float modelMatrix[16], bool depthHack ) { float modelViewMatrix[16]; - myGlMultMatrix( modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, - modelViewMatrix ); + const float* worldMVM = tr.viewDef->worldSpace.modelViewMatrix; + // DG: for r_lockSurfaces use the real world modelViewMatrix + // so GUIs don't float around + if(r_lockSurfaces.GetBool() && tr.viewDef == tr.primaryView) { + worldMVM = tr.lockSurfacesRealViewDef.worldSpace.modelViewMatrix; + } + + myGlMultMatrix( modelMatrix, worldMVM, modelViewMatrix ); for ( int i = 0 ; i < surfaces.Num() ; i++ ) { EmitSurface( &surfaces[i], modelMatrix, modelViewMatrix, depthHack ); diff --git a/neo/renderer/RenderSystem.cpp b/neo/renderer/RenderSystem.cpp index 08414a3a..2ab73451 100644 --- a/neo/renderer/RenderSystem.cpp +++ b/neo/renderer/RenderSystem.cpp @@ -221,11 +221,6 @@ void R_AddDrawViewCmd( viewDef_t *parms ) { cmd->viewDef = parms; - if ( parms->viewEntitys ) { - // save the command for r_lockSurfaces debugging - tr.lockSurfacesCmd = *cmd; - } - tr.pc.c_numViews++; R_ViewStatistics( parms ); @@ -235,43 +230,6 @@ void R_AddDrawViewCmd( viewDef_t *parms ) { //================================================================================= -/* -====================== -R_LockSurfaceScene - -r_lockSurfaces allows a developer to move around -without changing the composition of the scene, including -culling. The only thing that is modified is the -view position and axis, no front end work is done at all - - -Add the stored off command again, so the new rendering will use EXACTLY -the same surfaces, including all the culling, even though the transformation -matricies have been changed. This allow the culling tightness to be -evaluated interactively. -====================== -*/ -void R_LockSurfaceScene( viewDef_t *parms ) { - drawSurfsCommand_t *cmd; - viewEntity_t *vModel; - - // set the matrix for world space to eye space - R_SetViewMatrix( parms ); - tr.lockSurfacesCmd.viewDef->worldSpace = parms->worldSpace; - - // update the view origin and axis, and all - // the entity matricies - for( vModel = tr.lockSurfacesCmd.viewDef->viewEntitys ; vModel ; vModel = vModel->next ) { - myGlMultMatrix( vModel->modelMatrix, - tr.lockSurfacesCmd.viewDef->worldSpace.modelViewMatrix, - vModel->modelViewMatrix ); - } - - // add the stored off surface commands again - cmd = (drawSurfsCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) ); - *cmd = tr.lockSurfacesCmd; -} - /* ============= R_CheckCvars @@ -599,6 +557,18 @@ void idRenderSystemLocal::BeginFrame( int windowWidth, int windowHeight ) { return; } + // DG: r_lockSurfaces only works properly if r_useScissor is disabled + if ( r_lockSurfaces.IsModified() ) { + static bool origUseScissor = true; + r_lockSurfaces.ClearModified(); + if ( r_lockSurfaces.GetBool() ) { + origUseScissor = r_useScissor.GetBool(); + r_useScissor.SetBool( false ); + } else { + r_useScissor.SetBool( origUseScissor ); + } + } // DG end + // determine which back end we will use SetBackEndRenderer(); @@ -715,6 +685,7 @@ void idRenderSystemLocal::EndFrame( int *frontEndMsec, int *backEndMsec ) { // use the other buffers next frame, because another CPU // may still be rendering into the current buffers + R_ToggleSmpFrame(); // we can now release the vertexes used this frame diff --git a/neo/renderer/RenderWorld.cpp b/neo/renderer/RenderWorld.cpp index 227c96d2..1fc6e3b7 100644 --- a/neo/renderer/RenderWorld.cpp +++ b/neo/renderer/RenderWorld.cpp @@ -675,6 +675,8 @@ Rendering a scene may require multiple views to be rendered to handle mirrors, ==================== */ +extern void R_SetupViewFrustum( viewDef_t* viewDef ); +extern void R_SetupProjection( viewDef_t * viewDef ); void idRenderWorldLocal::RenderScene( const renderView_t *renderView ) { #ifndef ID_DEDICATED renderView_t copy; @@ -742,10 +744,32 @@ void idRenderWorldLocal::RenderScene( const renderView_t *renderView ) { } if ( r_lockSurfaces.GetBool() ) { - R_LockSurfaceScene( parms ); - return; + tr.lockSurfacesRealViewDef = *parms; + + // usually the following are called later in R_RenderView(), but we pass + // the locked viewDef to that function so do these calculations here + // (the results are needed for some special cases like in-world GUIs and mirrors) + R_SetViewMatrix( &tr.lockSurfacesRealViewDef ); + R_SetupViewFrustum( &tr.lockSurfacesRealViewDef); + R_SetupProjection( &tr.lockSurfacesRealViewDef ); + + const viewDef_t* origParms = &tr.lockSurfacesRealViewDef; + *parms = tr.lockSurfacesViewDef; + parms->renderWorld = origParms->renderWorld; + parms->floatTime = origParms->floatTime; + parms->drawSurfs = origParms->drawSurfs; // should be NULL I think + parms->numDrawSurfs = origParms->numDrawSurfs; + parms->maxDrawSurfs = origParms->maxDrawSurfs; + parms->viewLights = origParms->viewLights; + parms->viewEntitys = origParms->viewEntitys; + parms->connectedAreas = origParms->connectedAreas; + + } else { + // save current viewDef so it can be used if we enable r_lockSurfaces in the next frame + tr.lockSurfacesViewDef = *parms; } + // save this world for use by some console commands tr.primaryWorld = this; tr.primaryRenderView = *renderView; diff --git a/neo/renderer/tr_local.h b/neo/renderer/tr_local.h index f2a4d659..e97ed83f 100644 --- a/neo/renderer/tr_local.h +++ b/neo/renderer/tr_local.h @@ -548,7 +548,6 @@ extern frameData_t *frameData; //======================================================================= -void R_LockSurfaceScene( viewDef_t *parms ); void R_ClearCommandChain( void ); void R_AddDrawViewCmd( viewDef_t *parms ); @@ -785,6 +784,9 @@ public: performanceCounters_t pc; // performance counters drawSurfsCommand_t lockSurfacesCmd; // use this when r_lockSurfaces = 1 + //renderView_t lockSurfacesRenderView; + viewDef_t lockSurfacesViewDef; // of locked position/view + viewDef_t lockSurfacesRealViewDef; // of actual player position viewEntity_t identitySpace; // can use if we don't know viewDef->worldSpace is valid int stencilIncr, stencilDecr; // GL_INCR / INCR_WRAP_EXT, GL_DECR / GL_DECR_EXT diff --git a/neo/renderer/tr_main.cpp b/neo/renderer/tr_main.cpp index 3f3194eb..3fb5c02b 100644 --- a/neo/renderer/tr_main.cpp +++ b/neo/renderer/tr_main.cpp @@ -184,9 +184,6 @@ R_ToggleSmpFrame ==================== */ void R_ToggleSmpFrame( void ) { - if ( r_lockSurfaces.GetBool() ) { - return; - } R_FreeDeferredTriSurfs( frameData ); // clear frame-temporary data @@ -894,7 +891,7 @@ R_SetupProjection This uses the "infinite far z" trick =============== */ -void R_SetupProjection( void ) { +void R_SetupProjection( viewDef_t * viewDef ) { float xmin, xmax, ymin, ymax; float width, height; float zNear; @@ -915,48 +912,48 @@ void R_SetupProjection( void ) { // set up projection matrix // zNear = r_znear.GetFloat(); - if ( tr.viewDef->renderView.cramZNear ) { + if ( viewDef->renderView.cramZNear ) { zNear *= 0.25; } - ymax = zNear * tan( tr.viewDef->renderView.fov_y * idMath::PI / 360.0f ); + ymax = zNear * tan( viewDef->renderView.fov_y * idMath::PI / 360.0f ); ymin = -ymax; - xmax = zNear * tan( tr.viewDef->renderView.fov_x * idMath::PI / 360.0f ); + xmax = zNear * tan( viewDef->renderView.fov_x * idMath::PI / 360.0f ); xmin = -xmax; width = xmax - xmin; height = ymax - ymin; - jitterx = jitterx * width / ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 + 1 ); + jitterx = jitterx * width / ( viewDef->viewport.x2 - viewDef->viewport.x1 + 1 ); xmin += jitterx; xmax += jitterx; - jittery = jittery * height / ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 + 1 ); + jittery = jittery * height / ( viewDef->viewport.y2 - viewDef->viewport.y1 + 1 ); ymin += jittery; ymax += jittery; - tr.viewDef->projectionMatrix[0] = 2 * zNear / width; - tr.viewDef->projectionMatrix[4] = 0; - tr.viewDef->projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 - tr.viewDef->projectionMatrix[12] = 0; + viewDef->projectionMatrix[0] = 2 * zNear / width; + viewDef->projectionMatrix[4] = 0; + viewDef->projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 + viewDef->projectionMatrix[12] = 0; - tr.viewDef->projectionMatrix[1] = 0; - tr.viewDef->projectionMatrix[5] = 2 * zNear / height; - tr.viewDef->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 - tr.viewDef->projectionMatrix[13] = 0; + viewDef->projectionMatrix[1] = 0; + viewDef->projectionMatrix[5] = 2 * zNear / height; + viewDef->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 + viewDef->projectionMatrix[13] = 0; // this is the far-plane-at-infinity formulation, and // crunches the Z range slightly so w=0 vertexes do not // rasterize right at the wraparound point - tr.viewDef->projectionMatrix[2] = 0; - tr.viewDef->projectionMatrix[6] = 0; - tr.viewDef->projectionMatrix[10] = -0.999f; - tr.viewDef->projectionMatrix[14] = -2.0f * zNear; + viewDef->projectionMatrix[2] = 0; + viewDef->projectionMatrix[6] = 0; + viewDef->projectionMatrix[10] = -0.999f; + viewDef->projectionMatrix[14] = -2.0f * zNear; - tr.viewDef->projectionMatrix[3] = 0; - tr.viewDef->projectionMatrix[7] = 0; - tr.viewDef->projectionMatrix[11] = -1; - tr.viewDef->projectionMatrix[15] = 0; + viewDef->projectionMatrix[3] = 0; + viewDef->projectionMatrix[7] = 0; + viewDef->projectionMatrix[11] = -1; + viewDef->projectionMatrix[15] = 0; } /* @@ -967,30 +964,31 @@ Setup that culling frustum planes for the current view FIXME: derive from modelview matrix times projection matrix ================= */ -static void R_SetupViewFrustum( void ) { +//static +void R_SetupViewFrustum( viewDef_t* viewDef ) { int i; float xs, xc; float ang; - ang = DEG2RAD( tr.viewDef->renderView.fov_x ) * 0.5f; + ang = DEG2RAD( viewDef->renderView.fov_x ) * 0.5f; idMath::SinCos( ang, xs, xc ); - tr.viewDef->frustum[0] = xs * tr.viewDef->renderView.viewaxis[0] + xc * tr.viewDef->renderView.viewaxis[1]; - tr.viewDef->frustum[1] = xs * tr.viewDef->renderView.viewaxis[0] - xc * tr.viewDef->renderView.viewaxis[1]; + viewDef->frustum[0] = xs * viewDef->renderView.viewaxis[0] + xc * viewDef->renderView.viewaxis[1]; + viewDef->frustum[1] = xs * viewDef->renderView.viewaxis[0] - xc * viewDef->renderView.viewaxis[1]; - ang = DEG2RAD( tr.viewDef->renderView.fov_y ) * 0.5f; + ang = DEG2RAD( viewDef->renderView.fov_y ) * 0.5f; idMath::SinCos( ang, xs, xc ); - tr.viewDef->frustum[2] = xs * tr.viewDef->renderView.viewaxis[0] + xc * tr.viewDef->renderView.viewaxis[2]; - tr.viewDef->frustum[3] = xs * tr.viewDef->renderView.viewaxis[0] - xc * tr.viewDef->renderView.viewaxis[2]; + viewDef->frustum[2] = xs * viewDef->renderView.viewaxis[0] + xc * viewDef->renderView.viewaxis[2]; + viewDef->frustum[3] = xs * viewDef->renderView.viewaxis[0] - xc * viewDef->renderView.viewaxis[2]; // plane four is the front clipping plane - tr.viewDef->frustum[4] = /* vec3_origin - */ tr.viewDef->renderView.viewaxis[0]; + viewDef->frustum[4] = /* vec3_origin - */ viewDef->renderView.viewaxis[0]; for ( i = 0; i < 5; i++ ) { // flip direction so positive side faces out (FIXME: globally unify this) - tr.viewDef->frustum[i] = -tr.viewDef->frustum[i].Normal(); - tr.viewDef->frustum[i][3] = -( tr.viewDef->renderView.vieworg * tr.viewDef->frustum[i].Normal() ); + viewDef->frustum[i] = -viewDef->frustum[i].Normal(); + viewDef->frustum[i][3] = -( viewDef->renderView.vieworg * viewDef->frustum[i].Normal() ); } // eventually, plane five will be the rear clipping plane for fog @@ -998,16 +996,16 @@ static void R_SetupViewFrustum( void ) { float dNear, dFar, dLeft, dUp; dNear = r_znear.GetFloat(); - if ( tr.viewDef->renderView.cramZNear ) { + if ( viewDef->renderView.cramZNear ) { dNear *= 0.25f; } dFar = MAX_WORLD_SIZE; - dLeft = dFar * tan( DEG2RAD( tr.viewDef->renderView.fov_x * 0.5f ) ); - dUp = dFar * tan( DEG2RAD( tr.viewDef->renderView.fov_y * 0.5f ) ); - tr.viewDef->viewFrustum.SetOrigin( tr.viewDef->renderView.vieworg ); - tr.viewDef->viewFrustum.SetAxis( tr.viewDef->renderView.viewaxis ); - tr.viewDef->viewFrustum.SetSize( dNear, dFar, dLeft, dUp ); + dLeft = dFar * tan( DEG2RAD( viewDef->renderView.fov_x * 0.5f ) ); + dUp = dFar * tan( DEG2RAD( viewDef->renderView.fov_y * 0.5f ) ); + viewDef->viewFrustum.SetOrigin( viewDef->renderView.vieworg ); + viewDef->viewFrustum.SetAxis( viewDef->renderView.viewaxis ); + viewDef->viewFrustum.SetSize( dNear, dFar, dLeft, dUp ); } /* @@ -1115,11 +1113,11 @@ void R_RenderView( viewDef_t *parms ) { // the four sides of the view frustum are needed // for culling and portal visibility - R_SetupViewFrustum(); + R_SetupViewFrustum( tr.viewDef ); // we need to set the projection matrix before doing // portal-to-screen scissor box calculations - R_SetupProjection(); + R_SetupProjection( tr.viewDef ); // identify all the visible portalAreas, and the entityDefs and // lightDefs that are in them and pass culling. diff --git a/neo/renderer/tr_render.cpp b/neo/renderer/tr_render.cpp index 518cd051..2b7e10fc 100644 --- a/neo/renderer/tr_render.cpp +++ b/neo/renderer/tr_render.cpp @@ -554,23 +554,26 @@ to actually render the visible surfaces for this view ================= */ void RB_BeginDrawingView (void) { + + const viewDef_t* viewDef = backEnd.viewDef; + // set the modelview matrix for the viewer qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf( backEnd.viewDef->projectionMatrix ); + qglLoadMatrixf( viewDef->projectionMatrix ); qglMatrixMode(GL_MODELVIEW); // set the window clipping - qglViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1, - tr.viewportOffset[1] + backEnd.viewDef->viewport.y1, - backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1, - backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 ); + qglViewport( tr.viewportOffset[0] + viewDef->viewport.x1, + tr.viewportOffset[1] + viewDef->viewport.y1, + viewDef->viewport.x2 + 1 - viewDef->viewport.x1, + viewDef->viewport.y2 + 1 - viewDef->viewport.y1 ); // the scissor may be smaller than the viewport for subviews - qglScissor( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1 + backEnd.viewDef->scissor.x1, - tr.viewportOffset[1] + backEnd.viewDef->viewport.y1 + backEnd.viewDef->scissor.y1, - backEnd.viewDef->scissor.x2 + 1 - backEnd.viewDef->scissor.x1, - backEnd.viewDef->scissor.y2 + 1 - backEnd.viewDef->scissor.y1 ); - backEnd.currentScissor = backEnd.viewDef->scissor; + qglScissor( tr.viewportOffset[0] + viewDef->viewport.x1 + viewDef->scissor.x1, + tr.viewportOffset[1] + viewDef->viewport.y1 + viewDef->scissor.y1, + viewDef->scissor.x2 + 1 - viewDef->scissor.x1, + viewDef->scissor.y2 + 1 - viewDef->scissor.y1 ); + backEnd.currentScissor = viewDef->scissor; // ensures that depth writes are enabled for the depth clear GL_State( GLS_DEFAULT ); @@ -852,6 +855,31 @@ void RB_DrawView( const void *data ) { cmd = (const drawSurfsCommand_t *)data; + // with r_lockSurfaces enabled, we set the locked render view + // for the primary viewDef for all the "what should be drawn" calculations. + // now it must be reverted to the real render view so the scene gets rendered + // from the actual current players point of view + if(r_lockSurfaces.GetBool() && tr.primaryView == cmd->viewDef) { + viewDef_t* parms = cmd->viewDef; + const viewDef_t origParms = *parms; + + *parms = tr.lockSurfacesRealViewDef; // actual current player/camera position + parms->renderWorld = origParms.renderWorld; + parms->floatTime = origParms.floatTime; + parms->drawSurfs = origParms.drawSurfs; + parms->numDrawSurfs = origParms.numDrawSurfs; + parms->maxDrawSurfs = origParms.maxDrawSurfs; + parms->viewLights = origParms.viewLights; + parms->viewEntitys = origParms.viewEntitys; + parms->connectedAreas = origParms.connectedAreas; + + for( viewEntity_t* vModel = parms->viewEntitys ; vModel ; vModel = vModel->next ) { + myGlMultMatrix( vModel->modelMatrix, + parms->worldSpace.modelViewMatrix, + vModel->modelViewMatrix ); + } + } + backEnd.viewDef = cmd->viewDef; // we will need to do a new copyTexSubImage of the screen diff --git a/neo/renderer/tr_subview.cpp b/neo/renderer/tr_subview.cpp index 58c789a2..d3049cc6 100644 --- a/neo/renderer/tr_subview.cpp +++ b/neo/renderer/tr_subview.cpp @@ -490,6 +490,39 @@ bool R_GenerateSurfaceSubview( drawSurf_t *drawSurf ) { return false; } + // DG: r_lockSurfaces needs special treatment + if ( r_lockSurfaces.GetBool() && tr.viewDef == tr.primaryView ) { + // we need the scissor for the "real" viewDef (actual camera position etc) + // so mirrors don't "float around" when looking around with r_lockSurfaces enabled + // So do the same calculation as before, but with real viewDef (but don't replace + // calculation above, so the whole mirror or whatever is skipped if not visible in + // locked view!) + viewDef_t* origViewDef = tr.viewDef; + tr.viewDef = &tr.lockSurfacesRealViewDef; + R_PreciseCullSurface( drawSurf, ndcBounds ); + + idScreenRect origScissor = scissor; + + idScreenRect *v2 = &tr.viewDef->viewport; + scissor.x1 = v2->x1 + (int)( (v2->x2 - v2->x1 + 1 ) * 0.5f * ( ndcBounds[0][0] + 1.0f )); + scissor.y1 = v2->y1 + (int)( (v2->y2 - v2->y1 + 1 ) * 0.5f * ( ndcBounds[0][1] + 1.0f )); + scissor.x2 = v2->x1 + (int)( (v2->x2 - v2->x1 + 1 ) * 0.5f * ( ndcBounds[1][0] + 1.0f )); + scissor.y2 = v2->y1 + (int)( (v2->y2 - v2->y1 + 1 ) * 0.5f * ( ndcBounds[1][1] + 1.0f )); + + // nudge a bit for safety + scissor.Expand(); + + scissor.Intersect( tr.viewDef->scissor ); + + // TBH I'm not 100% happy with how this is handled - you won't get reliable information + // on what's rendered in a mirror this way. Intersecting with the orig. scissor looks "best". + // For handling this "properly" we'd need the whole "locked viewDef vs real viewDef" thing + // for every subview (instead of just once for the primaryView) which would be a lot of + // work for a corner case... + scissor.Intersect( origScissor ); + tr.viewDef = origViewDef; + } // DG end + // see what kind of subview we are making if ( shader->GetSort() != SS_SUBVIEW ) { for ( int i = 0 ; i < shader->GetNumStages() ; i++ ) {