doom3-bfg/neo/renderer/GuiModel.cpp

420 lines
12 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
2012-11-26 18:58:24 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
2012-11-26 18:58:24 +00:00
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 "precompiled.h"
2012-11-26 18:58:24 +00:00
#include "tr_local.h"
const float idGuiModel::STEREO_DEPTH_NEAR = 0.0f;
const float idGuiModel::STEREO_DEPTH_MID = 0.5f;
const float idGuiModel::STEREO_DEPTH_FAR = 1.0f;
/*
================
idGuiModel::idGuiModel
================
*/
idGuiModel::idGuiModel()
{
2012-11-26 18:58:24 +00:00
// identity color for drawsurf register evaluation
for( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ )
{
2012-11-26 18:58:24 +00:00
shaderParms[i] = 1.0f;
}
}
/*
================
idGuiModel::Clear
Begins collecting draw commands into surfaces
================
*/
void idGuiModel::Clear()
{
2012-11-26 18:58:24 +00:00
surfaces.SetNum( 0 );
AdvanceSurf();
}
/*
================
idGuiModel::WriteToDemo
================
*/
void idGuiModel::WriteToDemo( idDemoFile* demo )
{
2012-11-26 18:58:24 +00:00
}
/*
================
idGuiModel::ReadFromDemo
================
*/
void idGuiModel::ReadFromDemo( idDemoFile* demo )
{
2012-11-26 18:58:24 +00:00
}
/*
================
idGuiModel::BeginFrame
================
*/
void idGuiModel::BeginFrame()
{
2012-11-26 18:58:24 +00:00
vertexBlock = vertexCache.AllocVertex( NULL, ALIGN( MAX_VERTS * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
indexBlock = vertexCache.AllocIndex( NULL, ALIGN( MAX_INDEXES * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
vertexPointer = ( idDrawVert* )vertexCache.MappedVertexBuffer( vertexBlock );
indexPointer = ( triIndex_t* )vertexCache.MappedIndexBuffer( indexBlock );
2012-11-26 18:58:24 +00:00
numVerts = 0;
numIndexes = 0;
Clear();
}
idCVar stereoRender_defaultGuiDepth( "stereoRender_defaultGuiDepth", "0", CVAR_RENDERER, "Fraction of separation when not specified" );
/*
================
EmitSurfaces
For full screen GUIs, we can add in per-surface stereoscopic depth effects
================
*/
void idGuiModel::EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16],
bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity )
{
2012-11-26 18:58:24 +00:00
viewEntity_t* guiSpace = ( viewEntity_t* )R_ClearedFrameAlloc( sizeof( *guiSpace ), FRAME_ALLOC_VIEW_ENTITY );
2012-11-26 18:58:24 +00:00
memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) );
memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) );
guiSpace->weaponDepthHack = depthHack;
guiSpace->isGuiSurface = true;
2012-11-26 18:58:24 +00:00
// If this is an in-game gui, we need to be able to find the matrix again for head mounted
// display bypass matrix fixup.
if( linkAsEntity )
{
2012-11-26 18:58:24 +00:00
guiSpace->next = tr.viewDef->viewEntitys;
tr.viewDef->viewEntitys = guiSpace;
}
2012-11-26 18:58:24 +00:00
//---------------------------
// make a tech5 renderMatrix
//---------------------------
idRenderMatrix viewMat;
idRenderMatrix::Transpose( *( idRenderMatrix* )modelViewMatrix, viewMat );
2012-11-26 18:58:24 +00:00
idRenderMatrix::Multiply( tr.viewDef->projectionRenderMatrix, viewMat, guiSpace->mvp );
if( depthHack )
{
2012-11-26 18:58:24 +00:00
idRenderMatrix::ApplyDepthHack( guiSpace->mvp );
}
2012-11-26 18:58:24 +00:00
// to allow 3D-TV effects in the menu system, we define surface flags to set
// depth fractions between 0=screen and 1=infinity, which directly modulate the
// screenSeparation parameter for an X offset.
// The value is stored in the drawSurf sort value, which adjusts the matrix in the
// backend.
float defaultStereoDepth = stereoRender_defaultGuiDepth.GetFloat(); // default to at-screen
2012-11-26 18:58:24 +00:00
// add the surfaces to this view
for( int i = 0; i < surfaces.Num(); i++ )
{
const guiModelSurface_t& guiSurf = surfaces[i];
if( guiSurf.numIndexes == 0 )
{
2012-11-26 18:58:24 +00:00
continue;
}
const idMaterial* shader = guiSurf.material;
drawSurf_t* drawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE );
2012-11-26 18:58:24 +00:00
drawSurf->numIndexes = guiSurf.numIndexes;
drawSurf->ambientCache = vertexBlock;
// build a vertCacheHandle_t that points inside the allocated block
drawSurf->indexCache = indexBlock + ( ( int64 )( guiSurf.firstIndex * sizeof( triIndex_t ) ) << VERTCACHE_OFFSET_SHIFT );
drawSurf->shadowCache = 0;
2012-11-26 18:58:24 +00:00
drawSurf->jointCache = 0;
drawSurf->frontEndGeo = NULL;
drawSurf->space = guiSpace;
drawSurf->material = shader;
drawSurf->extraGLState = guiSurf.glState;
drawSurf->scissorRect = tr.viewDef->scissor;
drawSurf->sort = shader->GetSort();
drawSurf->renderZFail = 0;
// process the shader expressions for conditionals / color / texcoords
const float* constRegs = shader->ConstantRegisters();
if( constRegs )
{
2012-11-26 18:58:24 +00:00
// shader only uses constant values
drawSurf->shaderRegisters = constRegs;
}
else
{
float* regs = ( float* )R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER );
2012-11-26 18:58:24 +00:00
drawSurf->shaderRegisters = regs;
shader->EvaluateRegisters( regs, shaderParms, tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[1] * 0.001f, NULL );
}
R_LinkDrawSurfToView( drawSurf, tr.viewDef );
if( allowFullScreenStereoDepth )
{
2012-11-26 18:58:24 +00:00
// override sort with the stereoDepth
//drawSurf->sort = stereoDepth;
switch( guiSurf.stereoType )
{
case STEREO_DEPTH_TYPE_NEAR:
drawSurf->sort = STEREO_DEPTH_NEAR;
break;
case STEREO_DEPTH_TYPE_MID:
drawSurf->sort = STEREO_DEPTH_MID;
break;
case STEREO_DEPTH_TYPE_FAR:
drawSurf->sort = STEREO_DEPTH_FAR;
break;
case STEREO_DEPTH_TYPE_NONE:
default:
drawSurf->sort = defaultStereoDepth;
break;
2012-11-26 18:58:24 +00:00
}
}
}
}
/*
====================
EmitToCurrentView
====================
*/
void idGuiModel::EmitToCurrentView( float modelMatrix[16], bool depthHack )
{
2012-11-26 18:58:24 +00:00
float modelViewMatrix[16];
2012-11-26 18:58:24 +00:00
R_MatrixMultiply( modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, modelViewMatrix );
2012-11-26 18:58:24 +00:00
EmitSurfaces( modelMatrix, modelViewMatrix, depthHack, false /* stereoDepthSort */, true /* link as entity */ );
}
2012-12-22 05:15:50 +00:00
// DG: move function declaration here (=> out of EmitFullScreen() method) because it confused clang
// (and possibly other compilers that just didn't complain and silently made it a float variable
// initialized to something, probably 0.0f)
float GetScreenSeparationForGuis();
// DG end
2012-11-26 18:58:24 +00:00
/*
================
idGuiModel::EmitFullScreen
Creates a view that covers the screen and emit the surfaces
================
*/
void idGuiModel::EmitFullScreen()
{
2012-11-26 18:58:24 +00:00
if( surfaces[0].numIndexes == 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT( "Gui::EmitFullScreen" );
viewDef_t* viewDef = ( viewDef_t* )R_ClearedFrameAlloc( sizeof( *viewDef ), FRAME_ALLOC_VIEW_DEF );
2012-11-26 18:58:24 +00:00
viewDef->is2Dgui = true;
tr.GetCroppedViewport( &viewDef->viewport );
2012-11-26 18:58:24 +00:00
bool stereoEnabled = ( renderSystem->GetStereo3DMode() != STEREO3D_OFF );
if( stereoEnabled )
{
2012-11-26 18:58:24 +00:00
const float screenSeparation = GetScreenSeparationForGuis();
2012-11-26 18:58:24 +00:00
// this will be negated on the alternate eyes, both rendered each frame
viewDef->renderView.stereoScreenSeparation = screenSeparation;
2012-11-26 18:58:24 +00:00
extern idCVar stereoRender_swapEyes;
viewDef->renderView.viewEyeBuffer = 0; // render to both buffers
if( stereoRender_swapEyes.GetBool() )
{
2012-11-26 18:58:24 +00:00
viewDef->renderView.stereoScreenSeparation = -screenSeparation;
}
}
2012-11-26 18:58:24 +00:00
viewDef->scissor.x1 = 0;
viewDef->scissor.y1 = 0;
viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1;
viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1;
viewDef->projectionMatrix[0 * 4 + 0] = 2.0f / renderSystem->GetVirtualWidth();
viewDef->projectionMatrix[0 * 4 + 1] = 0.0f;
viewDef->projectionMatrix[0 * 4 + 2] = 0.0f;
viewDef->projectionMatrix[0 * 4 + 3] = 0.0f;
viewDef->projectionMatrix[1 * 4 + 0] = 0.0f;
viewDef->projectionMatrix[1 * 4 + 1] = -2.0f / renderSystem->GetVirtualHeight();
viewDef->projectionMatrix[1 * 4 + 2] = 0.0f;
viewDef->projectionMatrix[1 * 4 + 3] = 0.0f;
viewDef->projectionMatrix[2 * 4 + 0] = 0.0f;
viewDef->projectionMatrix[2 * 4 + 1] = 0.0f;
viewDef->projectionMatrix[2 * 4 + 2] = -2.0f;
viewDef->projectionMatrix[2 * 4 + 3] = 0.0f;
viewDef->projectionMatrix[3 * 4 + 0] = -1.0f;
viewDef->projectionMatrix[3 * 4 + 1] = 1.0f;
viewDef->projectionMatrix[3 * 4 + 2] = -1.0f;
viewDef->projectionMatrix[3 * 4 + 3] = 1.0f;
2012-11-26 18:58:24 +00:00
// make a tech5 renderMatrix for faster culling
idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->projectionMatrix, viewDef->projectionRenderMatrix );
viewDef->worldSpace.modelMatrix[0 * 4 + 0] = 1.0f;
viewDef->worldSpace.modelMatrix[1 * 4 + 1] = 1.0f;
viewDef->worldSpace.modelMatrix[2 * 4 + 2] = 1.0f;
viewDef->worldSpace.modelMatrix[3 * 4 + 3] = 1.0f;
viewDef->worldSpace.modelViewMatrix[0 * 4 + 0] = 1.0f;
viewDef->worldSpace.modelViewMatrix[1 * 4 + 1] = 1.0f;
viewDef->worldSpace.modelViewMatrix[2 * 4 + 2] = 1.0f;
viewDef->worldSpace.modelViewMatrix[3 * 4 + 3] = 1.0f;
2012-11-26 18:58:24 +00:00
viewDef->maxDrawSurfs = surfaces.Num();
viewDef->drawSurfs = ( drawSurf_t** )R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER );
2012-11-26 18:58:24 +00:00
viewDef->numDrawSurfs = 0;
viewDef_t* oldViewDef = tr.viewDef;
2012-11-26 18:58:24 +00:00
tr.viewDef = viewDef;
EmitSurfaces( viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix,
false /* depthHack */ , stereoEnabled /* stereoDepthSort */, false /* link as entity */ );
2012-11-26 18:58:24 +00:00
tr.viewDef = oldViewDef;
2012-11-26 18:58:24 +00:00
// add the command to draw this view
R_AddDrawViewCmd( viewDef, true );
}
/*
=============
AdvanceSurf
=============
*/
void idGuiModel::AdvanceSurf()
{
2012-11-26 18:58:24 +00:00
guiModelSurface_t s;
if( surfaces.Num() )
{
2012-11-26 18:58:24 +00:00
s.material = surf->material;
s.glState = surf->glState;
}
else
{
2012-11-26 18:58:24 +00:00
s.material = tr.defaultMaterial;
s.glState = 0;
}
2012-11-26 18:58:24 +00:00
// advance indexes so the pointer to each surface will be 16 byte aligned
numIndexes = ALIGN( numIndexes, 8 );
2012-11-26 18:58:24 +00:00
s.numIndexes = 0;
s.firstIndex = numIndexes;
2012-11-26 18:58:24 +00:00
surfaces.Append( s );
surf = &surfaces[ surfaces.Num() - 1 ];
}
/*
=============
AllocTris
=============
*/
idDrawVert* idGuiModel::AllocTris( int vertCount, const triIndex_t* tempIndexes, int indexCount, const idMaterial* material, const uint64 glState, const stereoDepthType_t stereoType )
{
if( material == NULL )
{
2012-11-26 18:58:24 +00:00
return NULL;
}
if( numIndexes + indexCount > MAX_INDEXES )
{
2012-11-26 18:58:24 +00:00
static int warningFrame = 0;
if( warningFrame != tr.frameCount )
{
2012-11-26 18:58:24 +00:00
warningFrame = tr.frameCount;
idLib::Warning( "idGuiModel::AllocTris: MAX_INDEXES exceeded" );
}
return NULL;
}
if( numVerts + vertCount > MAX_VERTS )
{
2012-11-26 18:58:24 +00:00
static int warningFrame = 0;
if( warningFrame != tr.frameCount )
{
2012-11-26 18:58:24 +00:00
warningFrame = tr.frameCount;
idLib::Warning( "idGuiModel::AllocTris: MAX_VERTS exceeded" );
}
return NULL;
}
2012-11-26 18:58:24 +00:00
// break the current surface if we are changing to a new material or we can't
// fit the data into our allocated block
if( material != surf->material || glState != surf->glState || stereoType != surf->stereoType )
{
if( surf->numIndexes )
{
2012-11-26 18:58:24 +00:00
AdvanceSurf();
}
surf->material = material;
surf->glState = glState;
surf->stereoType = stereoType;
}
2012-11-26 18:58:24 +00:00
int startVert = numVerts;
int startIndex = numIndexes;
2012-11-26 18:58:24 +00:00
numVerts += vertCount;
numIndexes += indexCount;
2012-11-26 18:58:24 +00:00
surf->numIndexes += indexCount;
if( ( startIndex & 1 ) || ( indexCount & 1 ) )
{
2012-11-26 18:58:24 +00:00
// slow for write combined memory!
// this should be very rare, since quads are always an even index count
for( int i = 0; i < indexCount; i++ )
{
2012-11-26 18:58:24 +00:00
indexPointer[startIndex + i] = startVert + tempIndexes[i];
}
}
else
{
for( int i = 0; i < indexCount; i += 2 )
{
WriteIndexPair( indexPointer + startIndex + i, startVert + tempIndexes[i], startVert + tempIndexes[i + 1] );
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
return vertexPointer + startVert;
}