GuiModel.cpp (12351B)
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 34 const float idGuiModel::STEREO_DEPTH_NEAR = 0.0f; 35 const float idGuiModel::STEREO_DEPTH_MID = 0.5f; 36 const float idGuiModel::STEREO_DEPTH_FAR = 1.0f; 37 38 /* 39 ================ 40 idGuiModel::idGuiModel 41 ================ 42 */ 43 idGuiModel::idGuiModel() { 44 // identity color for drawsurf register evaluation 45 for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ ) { 46 shaderParms[i] = 1.0f; 47 } 48 } 49 50 /* 51 ================ 52 idGuiModel::Clear 53 54 Begins collecting draw commands into surfaces 55 ================ 56 */ 57 void idGuiModel::Clear() { 58 surfaces.SetNum( 0 ); 59 AdvanceSurf(); 60 } 61 62 /* 63 ================ 64 idGuiModel::WriteToDemo 65 ================ 66 */ 67 void idGuiModel::WriteToDemo( idDemoFile *demo ) { 68 } 69 70 /* 71 ================ 72 idGuiModel::ReadFromDemo 73 ================ 74 */ 75 void idGuiModel::ReadFromDemo( idDemoFile *demo ) { 76 } 77 78 /* 79 ================ 80 idGuiModel::BeginFrame 81 ================ 82 */ 83 void idGuiModel::BeginFrame() { 84 vertexBlock = vertexCache.AllocVertex( NULL, ALIGN( MAX_VERTS * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); 85 indexBlock = vertexCache.AllocIndex( NULL, ALIGN( MAX_INDEXES * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); 86 vertexPointer = (idDrawVert *)vertexCache.MappedVertexBuffer( vertexBlock ); 87 indexPointer = (triIndex_t *)vertexCache.MappedIndexBuffer( indexBlock ); 88 numVerts = 0; 89 numIndexes = 0; 90 Clear(); 91 } 92 93 idCVar stereoRender_defaultGuiDepth( "stereoRender_defaultGuiDepth", "0", CVAR_RENDERER, "Fraction of separation when not specified" ); 94 /* 95 ================ 96 EmitSurfaces 97 98 For full screen GUIs, we can add in per-surface stereoscopic depth effects 99 ================ 100 */ 101 void idGuiModel::EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16], 102 bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity ) { 103 104 viewEntity_t * guiSpace = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *guiSpace ), FRAME_ALLOC_VIEW_ENTITY ); 105 memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) ); 106 memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) ); 107 guiSpace->weaponDepthHack = depthHack; 108 guiSpace->isGuiSurface = true; 109 110 // If this is an in-game gui, we need to be able to find the matrix again for head mounted 111 // display bypass matrix fixup. 112 if ( linkAsEntity ) { 113 guiSpace->next = tr.viewDef->viewEntitys; 114 tr.viewDef->viewEntitys = guiSpace; 115 } 116 117 //--------------------------- 118 // make a tech5 renderMatrix 119 //--------------------------- 120 idRenderMatrix viewMat; 121 idRenderMatrix::Transpose( *(idRenderMatrix *)modelViewMatrix, viewMat ); 122 idRenderMatrix::Multiply( tr.viewDef->projectionRenderMatrix, viewMat, guiSpace->mvp ); 123 if ( depthHack ) { 124 idRenderMatrix::ApplyDepthHack( guiSpace->mvp ); 125 } 126 127 // to allow 3D-TV effects in the menu system, we define surface flags to set 128 // depth fractions between 0=screen and 1=infinity, which directly modulate the 129 // screenSeparation parameter for an X offset. 130 // The value is stored in the drawSurf sort value, which adjusts the matrix in the 131 // backend. 132 float defaultStereoDepth = stereoRender_defaultGuiDepth.GetFloat(); // default to at-screen 133 134 // add the surfaces to this view 135 for ( int i = 0; i < surfaces.Num(); i++ ) { 136 const guiModelSurface_t & guiSurf = surfaces[i]; 137 if ( guiSurf.numIndexes == 0 ) { 138 continue; 139 } 140 141 const idMaterial * shader = guiSurf.material; 142 drawSurf_t * drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE ); 143 144 drawSurf->numIndexes = guiSurf.numIndexes; 145 drawSurf->ambientCache = vertexBlock; 146 // build a vertCacheHandle_t that points inside the allocated block 147 drawSurf->indexCache = indexBlock + ( (int64)(guiSurf.firstIndex*sizeof(triIndex_t)) << VERTCACHE_OFFSET_SHIFT ); 148 drawSurf->shadowCache = 0; 149 drawSurf->jointCache = 0; 150 drawSurf->frontEndGeo = NULL; 151 drawSurf->space = guiSpace; 152 drawSurf->material = shader; 153 drawSurf->extraGLState = guiSurf.glState; 154 drawSurf->scissorRect = tr.viewDef->scissor; 155 drawSurf->sort = shader->GetSort(); 156 drawSurf->renderZFail = 0; 157 // process the shader expressions for conditionals / color / texcoords 158 const float *constRegs = shader->ConstantRegisters(); 159 if ( constRegs ) { 160 // shader only uses constant values 161 drawSurf->shaderRegisters = constRegs; 162 } else { 163 float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER ); 164 drawSurf->shaderRegisters = regs; 165 shader->EvaluateRegisters( regs, shaderParms, tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[1] * 0.001f, NULL ); 166 } 167 R_LinkDrawSurfToView( drawSurf, tr.viewDef ); 168 if ( allowFullScreenStereoDepth ) { 169 // override sort with the stereoDepth 170 //drawSurf->sort = stereoDepth; 171 172 switch ( guiSurf.stereoType ) { 173 case STEREO_DEPTH_TYPE_NEAR: drawSurf->sort = STEREO_DEPTH_NEAR; break; 174 case STEREO_DEPTH_TYPE_MID: drawSurf->sort = STEREO_DEPTH_MID; break; 175 case STEREO_DEPTH_TYPE_FAR: drawSurf->sort = STEREO_DEPTH_FAR; break; 176 case STEREO_DEPTH_TYPE_NONE: 177 default: 178 drawSurf->sort = defaultStereoDepth; 179 break; 180 } 181 } 182 } 183 } 184 185 /* 186 ==================== 187 EmitToCurrentView 188 ==================== 189 */ 190 void idGuiModel::EmitToCurrentView( float modelMatrix[16], bool depthHack ) { 191 float modelViewMatrix[16]; 192 193 R_MatrixMultiply( modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, modelViewMatrix ); 194 195 EmitSurfaces( modelMatrix, modelViewMatrix, depthHack, false /* stereoDepthSort */, true /* link as entity */ ); 196 } 197 198 199 /* 200 ================ 201 idGuiModel::EmitFullScreen 202 203 Creates a view that covers the screen and emit the surfaces 204 ================ 205 */ 206 void idGuiModel::EmitFullScreen() { 207 208 if ( surfaces[0].numIndexes == 0 ) { 209 return; 210 } 211 212 SCOPED_PROFILE_EVENT( "Gui::EmitFullScreen" ); 213 214 viewDef_t * viewDef = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *viewDef ), FRAME_ALLOC_VIEW_DEF ); 215 viewDef->is2Dgui = true; 216 tr.GetCroppedViewport( &viewDef->viewport ); 217 218 bool stereoEnabled = ( renderSystem->GetStereo3DMode() != STEREO3D_OFF ); 219 if ( stereoEnabled ) { 220 float GetScreenSeparationForGuis(); 221 const float screenSeparation = GetScreenSeparationForGuis(); 222 223 // this will be negated on the alternate eyes, both rendered each frame 224 viewDef->renderView.stereoScreenSeparation = screenSeparation; 225 226 extern idCVar stereoRender_swapEyes; 227 viewDef->renderView.viewEyeBuffer = 0; // render to both buffers 228 if ( stereoRender_swapEyes.GetBool() ) { 229 viewDef->renderView.stereoScreenSeparation = -screenSeparation; 230 } 231 } 232 233 viewDef->scissor.x1 = 0; 234 viewDef->scissor.y1 = 0; 235 viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1; 236 viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1; 237 238 viewDef->projectionMatrix[0*4+0] = 2.0f / SCREEN_WIDTH; 239 viewDef->projectionMatrix[0*4+1] = 0.0f; 240 viewDef->projectionMatrix[0*4+2] = 0.0f; 241 viewDef->projectionMatrix[0*4+3] = 0.0f; 242 243 viewDef->projectionMatrix[1*4+0] = 0.0f; 244 viewDef->projectionMatrix[1*4+1] = -2.0f / SCREEN_HEIGHT; 245 viewDef->projectionMatrix[1*4+2] = 0.0f; 246 viewDef->projectionMatrix[1*4+3] = 0.0f; 247 248 viewDef->projectionMatrix[2*4+0] = 0.0f; 249 viewDef->projectionMatrix[2*4+1] = 0.0f; 250 viewDef->projectionMatrix[2*4+2] = -2.0f; 251 viewDef->projectionMatrix[2*4+3] = 0.0f; 252 253 viewDef->projectionMatrix[3*4+0] = -1.0f; 254 viewDef->projectionMatrix[3*4+1] = 1.0f; 255 viewDef->projectionMatrix[3*4+2] = -1.0f; 256 viewDef->projectionMatrix[3*4+3] = 1.0f; 257 258 // make a tech5 renderMatrix for faster culling 259 idRenderMatrix::Transpose( *(idRenderMatrix *)viewDef->projectionMatrix, viewDef->projectionRenderMatrix ); 260 261 viewDef->worldSpace.modelMatrix[0*4+0] = 1.0f; 262 viewDef->worldSpace.modelMatrix[1*4+1] = 1.0f; 263 viewDef->worldSpace.modelMatrix[2*4+2] = 1.0f; 264 viewDef->worldSpace.modelMatrix[3*4+3] = 1.0f; 265 266 viewDef->worldSpace.modelViewMatrix[0*4+0] = 1.0f; 267 viewDef->worldSpace.modelViewMatrix[1*4+1] = 1.0f; 268 viewDef->worldSpace.modelViewMatrix[2*4+2] = 1.0f; 269 viewDef->worldSpace.modelViewMatrix[3*4+3] = 1.0f; 270 271 viewDef->maxDrawSurfs = surfaces.Num(); 272 viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER ); 273 viewDef->numDrawSurfs = 0; 274 275 viewDef_t * oldViewDef = tr.viewDef; 276 tr.viewDef = viewDef; 277 278 EmitSurfaces( viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, 279 false /* depthHack */ , stereoEnabled /* stereoDepthSort */, false /* link as entity */ ); 280 281 tr.viewDef = oldViewDef; 282 283 // add the command to draw this view 284 R_AddDrawViewCmd( viewDef, true ); 285 } 286 287 /* 288 ============= 289 AdvanceSurf 290 ============= 291 */ 292 void idGuiModel::AdvanceSurf() { 293 guiModelSurface_t s; 294 295 if ( surfaces.Num() ) { 296 s.material = surf->material; 297 s.glState = surf->glState; 298 } else { 299 s.material = tr.defaultMaterial; 300 s.glState = 0; 301 } 302 303 // advance indexes so the pointer to each surface will be 16 byte aligned 304 numIndexes = ALIGN( numIndexes, 8 ); 305 306 s.numIndexes = 0; 307 s.firstIndex = numIndexes; 308 309 surfaces.Append( s ); 310 surf = &surfaces[ surfaces.Num() - 1 ]; 311 } 312 313 /* 314 ============= 315 AllocTris 316 ============= 317 */ 318 idDrawVert * idGuiModel::AllocTris( int vertCount, const triIndex_t * tempIndexes, int indexCount, const idMaterial * material, const uint64 glState, const stereoDepthType_t stereoType ) { 319 if ( material == NULL ) { 320 return NULL; 321 } 322 if ( numIndexes + indexCount > MAX_INDEXES ) { 323 static int warningFrame = 0; 324 if ( warningFrame != tr.frameCount ) { 325 warningFrame = tr.frameCount; 326 idLib::Warning( "idGuiModel::AllocTris: MAX_INDEXES exceeded" ); 327 } 328 return NULL; 329 } 330 if ( numVerts + vertCount > MAX_VERTS ) { 331 static int warningFrame = 0; 332 if ( warningFrame != tr.frameCount ) { 333 warningFrame = tr.frameCount; 334 idLib::Warning( "idGuiModel::AllocTris: MAX_VERTS exceeded" ); 335 } 336 return NULL; 337 } 338 339 // break the current surface if we are changing to a new material or we can't 340 // fit the data into our allocated block 341 if ( material != surf->material || glState != surf->glState || stereoType != surf->stereoType ) { 342 if ( surf->numIndexes ) { 343 AdvanceSurf(); 344 } 345 surf->material = material; 346 surf->glState = glState; 347 surf->stereoType = stereoType; 348 } 349 350 int startVert = numVerts; 351 int startIndex = numIndexes; 352 353 numVerts += vertCount; 354 numIndexes += indexCount; 355 356 surf->numIndexes += indexCount; 357 358 if ( ( startIndex & 1 ) || ( indexCount & 1 ) ) { 359 // slow for write combined memory! 360 // this should be very rare, since quads are always an even index count 361 for ( int i = 0; i < indexCount; i++ ) { 362 indexPointer[startIndex + i] = startVert + tempIndexes[i]; 363 } 364 } else { 365 for ( int i = 0; i < indexCount; i += 2 ) { 366 WriteIndexPair( indexPointer + startIndex + i, startVert + tempIndexes[i], startVert + tempIndexes[i+1] ); 367 } 368 } 369 370 return vertexPointer + startVert; 371 }