RenderWorld.cpp (58861B)
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 /* 35 =================== 36 R_ListRenderLightDefs_f 37 =================== 38 */ 39 void R_ListRenderLightDefs_f( const idCmdArgs &args ) { 40 int i; 41 idRenderLightLocal *ldef; 42 43 if ( !tr.primaryWorld ) { 44 return; 45 } 46 int active = 0; 47 int totalRef = 0; 48 int totalIntr = 0; 49 50 for ( i = 0; i < tr.primaryWorld->lightDefs.Num(); i++ ) { 51 ldef = tr.primaryWorld->lightDefs[i]; 52 if ( !ldef ) { 53 common->Printf( "%4i: FREED\n", i ); 54 continue; 55 } 56 57 // count up the interactions 58 int iCount = 0; 59 for ( idInteraction *inter = ldef->firstInteraction; inter != NULL; inter = inter->lightNext ) { 60 iCount++; 61 } 62 totalIntr += iCount; 63 64 // count up the references 65 int rCount = 0; 66 for ( areaReference_t *ref = ldef->references; ref; ref = ref->ownerNext ) { 67 rCount++; 68 } 69 totalRef += rCount; 70 71 common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, ldef->lightShader->GetName()); 72 active++; 73 } 74 75 common->Printf( "%i lightDefs, %i interactions, %i areaRefs\n", active, totalIntr, totalRef ); 76 } 77 78 /* 79 =================== 80 R_ListRenderEntityDefs_f 81 =================== 82 */ 83 void R_ListRenderEntityDefs_f( const idCmdArgs &args ) { 84 int i; 85 idRenderEntityLocal *mdef; 86 87 if ( !tr.primaryWorld ) { 88 return; 89 } 90 int active = 0; 91 int totalRef = 0; 92 int totalIntr = 0; 93 94 for ( i = 0; i < tr.primaryWorld->entityDefs.Num(); i++ ) { 95 mdef = tr.primaryWorld->entityDefs[i]; 96 if ( !mdef ) { 97 common->Printf( "%4i: FREED\n", i ); 98 continue; 99 } 100 101 // count up the interactions 102 int iCount = 0; 103 for ( idInteraction *inter = mdef->firstInteraction; inter != NULL; inter = inter->entityNext ) { 104 iCount++; 105 } 106 totalIntr += iCount; 107 108 // count up the references 109 int rCount = 0; 110 for ( areaReference_t *ref = mdef->entityRefs; ref; ref = ref->ownerNext ) { 111 rCount++; 112 } 113 totalRef += rCount; 114 115 common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, mdef->parms.hModel->Name()); 116 active++; 117 } 118 119 common->Printf( "total active: %i\n", active ); 120 } 121 122 /* 123 =================== 124 idRenderWorldLocal::idRenderWorldLocal 125 =================== 126 */ 127 idRenderWorldLocal::idRenderWorldLocal() { 128 mapName.Clear(); 129 mapTimeStamp = FILE_NOT_FOUND_TIMESTAMP; 130 131 generateAllInteractionsCalled = false; 132 133 areaNodes = NULL; 134 numAreaNodes = 0; 135 136 portalAreas = NULL; 137 numPortalAreas = 0; 138 139 doublePortals = NULL; 140 numInterAreaPortals = 0; 141 142 interactionTable = 0; 143 interactionTableWidth = 0; 144 interactionTableHeight = 0; 145 146 for ( int i = 0; i < decals.Num(); i++ ) { 147 decals[i].entityHandle = -1; 148 decals[i].lastStartTime = 0; 149 decals[i].decals = new (TAG_MODEL) idRenderModelDecal(); 150 } 151 152 for ( int i = 0; i < overlays.Num(); i++ ) { 153 overlays[i].entityHandle = -1; 154 overlays[i].lastStartTime = 0; 155 overlays[i].overlays = new (TAG_MODEL) idRenderModelOverlay(); 156 } 157 } 158 159 /* 160 =================== 161 idRenderWorldLocal::~idRenderWorldLocal 162 =================== 163 */ 164 idRenderWorldLocal::~idRenderWorldLocal() { 165 // free all the entityDefs, lightDefs, portals, etc 166 FreeWorld(); 167 168 for ( int i = 0; i < decals.Num(); i++ ) { 169 delete decals[i].decals; 170 } 171 172 for ( int i = 0; i < overlays.Num(); i++ ) { 173 delete overlays[i].overlays; 174 } 175 176 // free up the debug lines, polys, and text 177 RB_ClearDebugPolygons( 0 ); 178 RB_ClearDebugLines( 0 ); 179 RB_ClearDebugText( 0 ); 180 } 181 182 /* 183 =================== 184 ResizeInteractionTable 185 =================== 186 */ 187 void idRenderWorldLocal::ResizeInteractionTable() { 188 // we overflowed the interaction table, so make it larger 189 common->Printf( "idRenderWorldLocal::ResizeInteractionTable: overflowed interactionTable, resizing\n" ); 190 191 const int oldInteractionTableWidth = interactionTableWidth; 192 const int oldIinteractionTableHeight = interactionTableHeight; 193 idInteraction ** oldInteractionTable = interactionTable; 194 195 // build the interaction table 196 // this will be dynamically resized if the entity / light counts grow too much 197 interactionTableWidth = entityDefs.Num() + 100; 198 interactionTableHeight = lightDefs.Num() + 100; 199 const int size = interactionTableWidth * interactionTableHeight * sizeof( *interactionTable ); 200 interactionTable = (idInteraction **)R_ClearedStaticAlloc( size ); 201 for ( int l = 0; l < oldIinteractionTableHeight; l++ ) { 202 for ( int e = 0; e < oldInteractionTableWidth; e++ ) { 203 interactionTable[ l * interactionTableWidth + e ] = oldInteractionTable[ l * oldInteractionTableWidth + e ]; 204 } 205 } 206 207 R_StaticFree( oldInteractionTable ); 208 } 209 210 /* 211 =================== 212 AddEntityDef 213 =================== 214 */ 215 qhandle_t idRenderWorldLocal::AddEntityDef( const renderEntity_t *re ){ 216 // try and reuse a free spot 217 int entityHandle = entityDefs.FindNull(); 218 if ( entityHandle == -1 ) { 219 entityHandle = entityDefs.Append( NULL ); 220 221 if ( interactionTable && entityDefs.Num() > interactionTableWidth ) { 222 ResizeInteractionTable(); 223 } 224 } 225 226 UpdateEntityDef( entityHandle, re ); 227 228 return entityHandle; 229 } 230 231 /* 232 ============== 233 UpdateEntityDef 234 235 Does not write to the demo file, which will only be updated for 236 visible entities 237 ============== 238 */ 239 int c_callbackUpdate; 240 241 void idRenderWorldLocal::UpdateEntityDef( qhandle_t entityHandle, const renderEntity_t *re ) { 242 if ( r_skipUpdates.GetBool() ) { 243 return; 244 } 245 246 tr.pc.c_entityUpdates++; 247 248 if ( !re->hModel && !re->callback ) { 249 common->Error( "idRenderWorld::UpdateEntityDef: NULL hModel" ); 250 } 251 252 // create new slots if needed 253 if ( entityHandle < 0 || entityHandle > LUDICROUS_INDEX ) { 254 common->Error( "idRenderWorld::UpdateEntityDef: index = %i", entityHandle ); 255 } 256 while ( entityHandle >= entityDefs.Num() ) { 257 entityDefs.Append( NULL ); 258 } 259 260 idRenderEntityLocal *def = entityDefs[entityHandle]; 261 if ( def != NULL ) { 262 263 if ( !re->forceUpdate ) { 264 265 // check for exact match (OPTIMIZE: check through pointers more) 266 if ( !re->joints && !re->callbackData && !def->dynamicModel && !memcmp( re, &def->parms, sizeof( *re ) ) ) { 267 return; 268 } 269 270 // if the only thing that changed was shaderparms, we can just leave things as they are 271 // after updating parms 272 273 // if we have a callback function and the bounds, origin, axis and model match, 274 // then we can leave the references as they are 275 if ( re->callback ) { 276 277 bool axisMatch = ( re->axis == def->parms.axis ); 278 bool originMatch = ( re->origin == def->parms.origin ); 279 bool boundsMatch = ( re->bounds == def->localReferenceBounds ); 280 bool modelMatch = ( re->hModel == def->parms.hModel ); 281 282 if ( boundsMatch && originMatch && axisMatch && modelMatch ) { 283 // only clear the dynamic model and interaction surfaces if they exist 284 c_callbackUpdate++; 285 R_ClearEntityDefDynamicModel( def ); 286 def->parms = *re; 287 return; 288 } 289 } 290 } 291 292 // save any decals if the model is the same, allowing marks to move with entities 293 if ( def->parms.hModel == re->hModel ) { 294 R_FreeEntityDefDerivedData( def, true, true ); 295 } else { 296 R_FreeEntityDefDerivedData( def, false, false ); 297 } 298 } else { 299 // creating a new one 300 def = new (TAG_RENDER_ENTITY) idRenderEntityLocal; 301 entityDefs[entityHandle] = def; 302 303 def->world = this; 304 def->index = entityHandle; 305 } 306 307 def->parms = *re; 308 309 def->lastModifiedFrameNum = tr.frameCount; 310 if ( common->WriteDemo() && def->archived ) { 311 WriteFreeEntity( entityHandle ); 312 def->archived = false; 313 } 314 315 // optionally immediately issue any callbacks 316 if ( !r_useEntityCallbacks.GetBool() && def->parms.callback != NULL ) { 317 R_IssueEntityDefCallback( def ); 318 } 319 320 // trigger entities don't need to get linked in and processed, 321 // they only exist for editor use 322 if ( def->parms.hModel != NULL && !def->parms.hModel->ModelHasDrawingSurfaces() ) { 323 return; 324 } 325 326 // based on the model bounds, add references in each area 327 // that may contain the updated surface 328 R_CreateEntityRefs( def ); 329 } 330 331 /* 332 =================== 333 FreeEntityDef 334 335 Frees all references and lit surfaces from the model, and 336 NULL's out it's entry in the world list 337 =================== 338 */ 339 void idRenderWorldLocal::FreeEntityDef( qhandle_t entityHandle ) { 340 idRenderEntityLocal *def; 341 342 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 343 common->Printf( "idRenderWorld::FreeEntityDef: handle %i > %i\n", entityHandle, entityDefs.Num() ); 344 return; 345 } 346 347 def = entityDefs[entityHandle]; 348 if ( !def ) { 349 common->Printf( "idRenderWorld::FreeEntityDef: handle %i is NULL\n", entityHandle ); 350 return; 351 } 352 353 R_FreeEntityDefDerivedData( def, false, false ); 354 355 if ( common->WriteDemo() && def->archived ) { 356 WriteFreeEntity( entityHandle ); 357 } 358 359 // if we are playing a demo, these will have been freed 360 // in R_FreeEntityDefDerivedData(), otherwise the gui 361 // object still exists in the game 362 363 def->parms.gui[ 0 ] = NULL; 364 def->parms.gui[ 1 ] = NULL; 365 def->parms.gui[ 2 ] = NULL; 366 367 delete def; 368 entityDefs[ entityHandle ] = NULL; 369 } 370 371 /* 372 ================== 373 GetRenderEntity 374 ================== 375 */ 376 const renderEntity_t *idRenderWorldLocal::GetRenderEntity( qhandle_t entityHandle ) const { 377 idRenderEntityLocal *def; 378 379 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 380 common->Printf( "idRenderWorld::GetRenderEntity: invalid handle %i [0, %i]\n", entityHandle, entityDefs.Num() ); 381 return NULL; 382 } 383 384 def = entityDefs[entityHandle]; 385 if ( !def ) { 386 common->Printf( "idRenderWorld::GetRenderEntity: handle %i is NULL\n", entityHandle ); 387 return NULL; 388 } 389 390 return &def->parms; 391 } 392 393 /* 394 ================== 395 AddLightDef 396 ================== 397 */ 398 qhandle_t idRenderWorldLocal::AddLightDef( const renderLight_t *rlight ) { 399 // try and reuse a free spot 400 int lightHandle = lightDefs.FindNull(); 401 402 if ( lightHandle == -1 ) { 403 lightHandle = lightDefs.Append( NULL ); 404 if ( interactionTable && lightDefs.Num() > interactionTableHeight ) { 405 ResizeInteractionTable(); 406 } 407 } 408 UpdateLightDef( lightHandle, rlight ); 409 410 return lightHandle; 411 } 412 413 /* 414 ================= 415 UpdateLightDef 416 417 The generation of all the derived interaction data will 418 usually be deferred until it is visible in a scene 419 420 Does not write to the demo file, which will only be done for visible lights 421 ================= 422 */ 423 void idRenderWorldLocal::UpdateLightDef( qhandle_t lightHandle, const renderLight_t *rlight ) { 424 if ( r_skipUpdates.GetBool() ) { 425 return; 426 } 427 428 tr.pc.c_lightUpdates++; 429 430 // create new slots if needed 431 if ( lightHandle < 0 || lightHandle > LUDICROUS_INDEX ) { 432 common->Error( "idRenderWorld::UpdateLightDef: index = %i", lightHandle ); 433 } 434 while ( lightHandle >= lightDefs.Num() ) { 435 lightDefs.Append( NULL ); 436 } 437 438 bool justUpdate = false; 439 idRenderLightLocal *light = lightDefs[lightHandle]; 440 if ( light ) { 441 // if the shape of the light stays the same, we don't need to dump 442 // any of our derived data, because shader parms are calculated every frame 443 if ( rlight->axis == light->parms.axis && rlight->end == light->parms.end && 444 rlight->lightCenter == light->parms.lightCenter && rlight->lightRadius == light->parms.lightRadius && 445 rlight->noShadows == light->parms.noShadows && rlight->origin == light->parms.origin && 446 rlight->parallel == light->parms.parallel && rlight->pointLight == light->parms.pointLight && 447 rlight->right == light->parms.right && rlight->start == light->parms.start && 448 rlight->target == light->parms.target && rlight->up == light->parms.up && 449 rlight->shader == light->lightShader && rlight->prelightModel == light->parms.prelightModel ) { 450 justUpdate = true; 451 } else { 452 // if we are updating shadows, the prelight model is no longer valid 453 light->lightHasMoved = true; 454 R_FreeLightDefDerivedData( light ); 455 } 456 } else { 457 // create a new one 458 light = new (TAG_RENDER_LIGHT) idRenderLightLocal; 459 lightDefs[lightHandle] = light; 460 461 light->world = this; 462 light->index = lightHandle; 463 } 464 465 light->parms = *rlight; 466 light->lastModifiedFrameNum = tr.frameCount; 467 if ( common->WriteDemo() && light->archived ) { 468 WriteFreeLight( lightHandle ); 469 light->archived = false; 470 } 471 472 // new for BFG edition: force noShadows on spectrum lights so teleport spawns 473 // don't cause such a slowdown. Hell writing shouldn't be shadowed anyway... 474 if ( light->parms.shader && light->parms.shader->Spectrum() ) { 475 light->parms.noShadows = true; 476 } 477 478 if ( light->lightHasMoved ) { 479 light->parms.prelightModel = NULL; 480 } 481 482 if ( !justUpdate ) { 483 R_CreateLightRefs( light ); 484 } 485 } 486 487 /* 488 ==================== 489 FreeLightDef 490 491 Frees all references and lit surfaces from the light, and 492 NULL's out it's entry in the world list 493 ==================== 494 */ 495 void idRenderWorldLocal::FreeLightDef( qhandle_t lightHandle ) { 496 idRenderLightLocal *light; 497 498 if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) { 499 common->Printf( "idRenderWorld::FreeLightDef: invalid handle %i [0, %i]\n", lightHandle, lightDefs.Num() ); 500 return; 501 } 502 503 light = lightDefs[lightHandle]; 504 if ( !light ) { 505 common->Printf( "idRenderWorld::FreeLightDef: handle %i is NULL\n", lightHandle ); 506 return; 507 } 508 509 R_FreeLightDefDerivedData( light ); 510 511 if ( common->WriteDemo() && light->archived ) { 512 WriteFreeLight( lightHandle ); 513 } 514 515 delete light; 516 lightDefs[lightHandle] = NULL; 517 } 518 519 /* 520 ================== 521 GetRenderLight 522 ================== 523 */ 524 const renderLight_t *idRenderWorldLocal::GetRenderLight( qhandle_t lightHandle ) const { 525 idRenderLightLocal *def; 526 527 if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) { 528 common->Printf( "idRenderWorld::GetRenderLight: handle %i > %i\n", lightHandle, lightDefs.Num() ); 529 return NULL; 530 } 531 532 def = lightDefs[lightHandle]; 533 if ( !def ) { 534 common->Printf( "idRenderWorld::GetRenderLight: handle %i is NULL\n", lightHandle ); 535 return NULL; 536 } 537 538 return &def->parms; 539 } 540 541 /* 542 ================ 543 idRenderWorldLocal::ProjectDecalOntoWorld 544 ================ 545 */ 546 void idRenderWorldLocal::ProjectDecalOntoWorld( const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) { 547 decalProjectionParms_t globalParms; 548 549 if ( !idRenderModelDecal::CreateProjectionParms( globalParms, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) { 550 return; 551 } 552 553 // get the world areas touched by the projection volume 554 int areas[10]; 555 int numAreas = BoundsInAreas( globalParms.projectionBounds, areas, 10 ); 556 557 // check all areas for models 558 for ( int i = 0; i < numAreas; i++ ) { 559 560 const portalArea_t * area = &portalAreas[ areas[i] ]; 561 562 // check all models in this area 563 for ( const areaReference_t * ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) { 564 idRenderEntityLocal * def = ref->entity; 565 566 if ( def->parms.noOverlays ) { 567 continue; 568 } 569 570 if ( def->parms.customShader != NULL && !def->parms.customShader->AllowOverlays() ) { 571 continue; 572 } 573 574 // completely ignore any dynamic or callback models 575 const idRenderModel * model = def->parms.hModel; 576 if ( def->parms.callback != NULL || model == NULL || model->IsDynamicModel() != DM_STATIC ) { 577 continue; 578 } 579 580 idBounds bounds; 581 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis ); 582 583 // if the model bounds do not overlap with the projection bounds 584 decalProjectionParms_t localParms; 585 if ( !globalParms.projectionBounds.IntersectsBounds( bounds ) ) { 586 continue; 587 } 588 589 // transform the bounding planes, fade planes and texture axis into local space 590 idRenderModelDecal::GlobalProjectionParmsToLocal( localParms, globalParms, def->parms.origin, def->parms.axis ); 591 localParms.force = ( def->parms.customShader != NULL ); 592 593 if ( def->decals == NULL ) { 594 def->decals = AllocDecal( def->index, startTime ); 595 } 596 def->decals->AddDeferredDecal( localParms ); 597 } 598 } 599 } 600 601 /* 602 ==================== 603 idRenderWorldLocal::ProjectDecal 604 ==================== 605 */ 606 void idRenderWorldLocal::ProjectDecal( qhandle_t entityHandle, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) { 607 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 608 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle ); 609 return; 610 } 611 612 idRenderEntityLocal *def = entityDefs[ entityHandle ]; 613 if ( def == NULL ) { 614 return; 615 } 616 617 const idRenderModel *model = def->parms.hModel; 618 619 if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback != NULL ) { 620 return; 621 } 622 623 decalProjectionParms_t globalParms; 624 if ( !idRenderModelDecal::CreateProjectionParms( globalParms, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) { 625 return; 626 } 627 628 idBounds bounds; 629 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis ); 630 631 // if the model bounds do not overlap with the projection bounds 632 if ( !globalParms.projectionBounds.IntersectsBounds( bounds ) ) { 633 return; 634 } 635 636 // transform the bounding planes, fade planes and texture axis into local space 637 decalProjectionParms_t localParms; 638 idRenderModelDecal::GlobalProjectionParmsToLocal( localParms, globalParms, def->parms.origin, def->parms.axis ); 639 localParms.force = ( def->parms.customShader != NULL ); 640 641 if ( def->decals == NULL ) { 642 def->decals = AllocDecal( def->index, startTime ); 643 } 644 def->decals->AddDeferredDecal( localParms ); 645 } 646 647 /* 648 ==================== 649 idRenderWorldLocal::ProjectOverlay 650 ==================== 651 */ 652 void idRenderWorldLocal::ProjectOverlay( qhandle_t entityHandle, const idPlane localTextureAxis[2], const idMaterial *material, const int startTime ) { 653 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 654 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle ); 655 return; 656 } 657 658 idRenderEntityLocal *def = entityDefs[ entityHandle ]; 659 if ( def == NULL ) { 660 return; 661 } 662 663 const idRenderModel * model = def->parms.hModel; 664 if ( model->IsDynamicModel() != DM_CACHED ) { // FIXME: probably should be MD5 only 665 return; 666 } 667 668 overlayProjectionParms_t localParms; 669 localParms.localTextureAxis[0] = localTextureAxis[0]; 670 localParms.localTextureAxis[1] = localTextureAxis[1]; 671 localParms.material = material; 672 localParms.startTime = startTime; 673 674 if ( def->overlays == NULL ) { 675 def->overlays = AllocOverlay( def->index, startTime ); 676 } 677 def->overlays->AddDeferredOverlay( localParms ); 678 } 679 680 /* 681 ==================== 682 idRenderWorldLocal::AllocDecal 683 ==================== 684 */ 685 idRenderModelDecal * idRenderWorldLocal::AllocDecal( qhandle_t newEntityHandle, int startTime ) { 686 int oldest = 0; 687 int oldestTime = MAX_TYPE( oldestTime ); 688 for ( int i = 0; i < decals.Num(); i++ ) { 689 if ( decals[i].lastStartTime < oldestTime ) { 690 oldestTime = decals[i].lastStartTime; 691 oldest = i; 692 } 693 } 694 695 // remove any reference another model may still have to this decal 696 if ( decals[oldest].entityHandle >= 0 && decals[oldest].entityHandle < entityDefs.Num() ) { 697 idRenderEntityLocal *def = entityDefs[decals[oldest].entityHandle]; 698 if ( def != NULL && def->decals == decals[oldest].decals ) { 699 def->decals = NULL; 700 } 701 } 702 703 decals[oldest].entityHandle = newEntityHandle; 704 decals[oldest].lastStartTime = startTime; 705 decals[oldest].decals->ReUse(); 706 707 return decals[oldest].decals; 708 } 709 710 /* 711 ==================== 712 idRenderWorldLocal::AllocOverlay 713 ==================== 714 */ 715 idRenderModelOverlay * idRenderWorldLocal::AllocOverlay( qhandle_t newEntityHandle, int startTime ) { 716 int oldest = 0; 717 int oldestTime = MAX_TYPE( oldestTime ); 718 for ( int i = 0; i < overlays.Num(); i++ ) { 719 if ( overlays[i].lastStartTime < oldestTime ) { 720 oldestTime = overlays[i].lastStartTime; 721 oldest = i; 722 } 723 } 724 725 // remove any reference another model may still have to this overlay 726 if ( overlays[oldest].entityHandle >= 0 && overlays[oldest].entityHandle < entityDefs.Num() ) { 727 idRenderEntityLocal *def = entityDefs[overlays[oldest].entityHandle]; 728 if ( def != NULL && def->overlays == overlays[oldest].overlays ) { 729 def->overlays = NULL; 730 } 731 } 732 733 overlays[oldest].entityHandle = newEntityHandle; 734 overlays[oldest].lastStartTime = startTime; 735 overlays[oldest].overlays->ReUse(); 736 737 return overlays[oldest].overlays; 738 } 739 740 /* 741 ==================== 742 idRenderWorldLocal::RemoveDecals 743 ==================== 744 */ 745 void idRenderWorldLocal::RemoveDecals( qhandle_t entityHandle ) { 746 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 747 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle ); 748 return; 749 } 750 751 idRenderEntityLocal *def = entityDefs[ entityHandle ]; 752 if ( !def ) { 753 return; 754 } 755 756 R_FreeEntityDefDecals( def ); 757 R_FreeEntityDefOverlay( def ); 758 } 759 760 /* 761 ==================== 762 idRenderWorldLocal::SetRenderView 763 764 Sets the current view so any calls to the render world will use the correct parms. 765 ==================== 766 */ 767 void idRenderWorldLocal::SetRenderView( const renderView_t *renderView ) { 768 tr.primaryRenderView = *renderView; 769 } 770 771 /* 772 ==================== 773 idRenderWorldLocal::RenderScene 774 775 Draw a 3D view into a part of the window, then return 776 to 2D drawing. 777 778 Rendering a scene may require multiple views to be rendered 779 to handle mirrors, 780 ==================== 781 */ 782 void idRenderWorldLocal::RenderScene( const renderView_t *renderView ) { 783 if ( !R_IsInitialized() ) { 784 return; 785 } 786 787 renderView_t copy = *renderView; 788 789 // skip front end rendering work, which will result 790 // in only gui drawing 791 if ( r_skipFrontEnd.GetBool() ) { 792 return; 793 } 794 795 SCOPED_PROFILE_EVENT( "RenderWorld::RenderScene" ); 796 797 if ( renderView->fov_x <= 0 || renderView->fov_y <= 0 ) { 798 common->Error( "idRenderWorld::RenderScene: bad FOVs: %f, %f", renderView->fov_x, renderView->fov_y ); 799 } 800 801 // close any gui drawing 802 tr.guiModel->EmitFullScreen(); 803 tr.guiModel->Clear(); 804 805 int startTime = Sys_Microseconds(); 806 807 // setup view parms for the initial view 808 viewDef_t * parms = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *parms ), FRAME_ALLOC_VIEW_DEF ); 809 parms->renderView = *renderView; 810 811 if ( tr.takingScreenshot ) { 812 parms->renderView.forceUpdate = true; 813 } 814 815 int windowWidth = tr.GetWidth(); 816 int windowHeight = tr.GetHeight(); 817 tr.PerformResolutionScaling( windowWidth, windowHeight ); 818 819 // screenFraction is just for quickly testing fill rate limitations 820 if ( r_screenFraction.GetInteger() != 100 ) { 821 windowWidth = ( windowWidth * r_screenFraction.GetInteger() ) / 100; 822 windowHeight = ( windowHeight * r_screenFraction.GetInteger() ) / 100; 823 } 824 tr.CropRenderSize( windowWidth, windowHeight ); 825 tr.GetCroppedViewport( &parms->viewport ); 826 827 // the scissor bounds may be shrunk in subviews even if 828 // the viewport stays the same 829 // this scissor range is local inside the viewport 830 parms->scissor.x1 = 0; 831 parms->scissor.y1 = 0; 832 parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1; 833 parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1; 834 835 parms->isSubview = false; 836 parms->initialViewAreaOrigin = renderView->vieworg; 837 parms->renderWorld = this; 838 839 // see if the view needs to reverse the culling sense in mirrors 840 // or environment cube sides 841 idVec3 cross; 842 cross = parms->renderView.viewaxis[1].Cross( parms->renderView.viewaxis[2] ); 843 if ( cross * parms->renderView.viewaxis[0] > 0 ) { 844 parms->isMirror = false; 845 } else { 846 parms->isMirror = true; 847 } 848 849 // save this world for use by some console commands 850 tr.primaryWorld = this; 851 tr.primaryRenderView = *renderView; 852 tr.primaryView = parms; 853 854 // rendering this view may cause other views to be rendered 855 // for mirrors / portals / shadows / environment maps 856 // this will also cause any necessary entities and lights to be 857 // updated to the demo file 858 R_RenderView( parms ); 859 860 // render any post processing after the view and all its subviews has been draw 861 R_RenderPostProcess( parms ); 862 863 // now write delete commands for any modified-but-not-visible entities, and 864 // add the renderView command to the demo 865 if ( common->WriteDemo() ) { 866 WriteRenderView( renderView ); 867 } 868 869 #if 0 870 for ( int i = 0; i < entityDefs.Num(); i++ ) { 871 idRenderEntityLocal *def = entityDefs[i]; 872 if ( !def ) { 873 continue; 874 } 875 if ( def->parms.callback ) { 876 continue; 877 } 878 if ( def->parms.hModel->IsDynamicModel() == DM_CONTINUOUS ) { 879 } 880 } 881 #endif 882 883 tr.UnCrop(); 884 885 int endTime = Sys_Microseconds(); 886 887 tr.pc.frontEndMicroSec += endTime - startTime; 888 889 // prepare for any 2D drawing after this 890 tr.guiModel->Clear(); 891 } 892 893 /* 894 =================== 895 idRenderWorldLocal::NumAreas 896 =================== 897 */ 898 int idRenderWorldLocal::NumAreas() const { 899 return numPortalAreas; 900 } 901 902 /* 903 =================== 904 idRenderWorldLocal::NumPortalsInArea 905 =================== 906 */ 907 int idRenderWorldLocal::NumPortalsInArea( int areaNum ) { 908 portalArea_t *area; 909 int count; 910 portal_t *portal; 911 912 if ( areaNum >= numPortalAreas || areaNum < 0 ) { 913 common->Error( "idRenderWorld::NumPortalsInArea: bad areanum %i", areaNum ); 914 } 915 area = &portalAreas[areaNum]; 916 917 count = 0; 918 for ( portal = area->portals; portal; portal = portal->next ) { 919 count++; 920 } 921 return count; 922 } 923 924 /* 925 =================== 926 idRenderWorldLocal::GetPortal 927 =================== 928 */ 929 exitPortal_t idRenderWorldLocal::GetPortal( int areaNum, int portalNum ) { 930 portalArea_t *area; 931 int count; 932 portal_t *portal; 933 exitPortal_t ret; 934 935 if ( areaNum > numPortalAreas ) { 936 common->Error( "idRenderWorld::GetPortal: areaNum > numAreas" ); 937 } 938 area = &portalAreas[areaNum]; 939 940 count = 0; 941 for ( portal = area->portals; portal; portal = portal->next ) { 942 if ( count == portalNum ) { 943 ret.areas[0] = areaNum; 944 ret.areas[1] = portal->intoArea; 945 ret.w = portal->w; 946 ret.blockingBits = portal->doublePortal->blockingBits; 947 ret.portalHandle = portal->doublePortal - doublePortals + 1; 948 return ret; 949 } 950 count++; 951 } 952 953 common->Error( "idRenderWorld::GetPortal: portalNum > numPortals" ); 954 955 memset( &ret, 0, sizeof( ret ) ); 956 return ret; 957 } 958 959 /* 960 =============== 961 idRenderWorldLocal::PointInAreaNum 962 963 Will return -1 if the point is not in an area, otherwise 964 it will return 0 <= value < tr.world->numPortalAreas 965 =============== 966 */ 967 int idRenderWorldLocal::PointInArea( const idVec3 &point ) const { 968 areaNode_t *node; 969 int nodeNum; 970 float d; 971 972 node = areaNodes; 973 if ( !node ) { 974 return -1; 975 } 976 while( 1 ) { 977 d = point * node->plane.Normal() + node->plane[3]; 978 if (d > 0) { 979 nodeNum = node->children[0]; 980 } else { 981 nodeNum = node->children[1]; 982 } 983 if ( nodeNum == 0 ) { 984 return -1; // in solid 985 } 986 if ( nodeNum < 0 ) { 987 nodeNum = -1 - nodeNum; 988 if ( nodeNum >= numPortalAreas ) { 989 common->Error( "idRenderWorld::PointInArea: area out of range" ); 990 } 991 return nodeNum; 992 } 993 node = areaNodes + nodeNum; 994 } 995 996 return -1; 997 } 998 999 /* 1000 =================== 1001 idRenderWorldLocal::BoundsInAreas_r 1002 =================== 1003 */ 1004 void idRenderWorldLocal::BoundsInAreas_r( int nodeNum, const idBounds &bounds, int *areas, int *numAreas, int maxAreas ) const { 1005 int side, i; 1006 areaNode_t *node; 1007 1008 do { 1009 if ( nodeNum < 0 ) { 1010 nodeNum = -1 - nodeNum; 1011 1012 for ( i = 0; i < (*numAreas); i++ ) { 1013 if ( areas[i] == nodeNum ) { 1014 break; 1015 } 1016 } 1017 if ( i >= (*numAreas) && (*numAreas) < maxAreas ) { 1018 areas[(*numAreas)++] = nodeNum; 1019 } 1020 return; 1021 } 1022 1023 node = areaNodes + nodeNum; 1024 1025 side = bounds.PlaneSide( node->plane ); 1026 if ( side == PLANESIDE_FRONT ) { 1027 nodeNum = node->children[0]; 1028 } 1029 else if ( side == PLANESIDE_BACK ) { 1030 nodeNum = node->children[1]; 1031 } 1032 else { 1033 if ( node->children[1] != 0 ) { 1034 BoundsInAreas_r( node->children[1], bounds, areas, numAreas, maxAreas ); 1035 if ( (*numAreas) >= maxAreas ) { 1036 return; 1037 } 1038 } 1039 nodeNum = node->children[0]; 1040 } 1041 } while( nodeNum != 0 ); 1042 1043 return; 1044 } 1045 1046 /* 1047 =================== 1048 idRenderWorldLocal::BoundsInAreas 1049 1050 fills the *areas array with the number of the areas the bounds are in 1051 returns the total number of areas the bounds are in 1052 =================== 1053 */ 1054 int idRenderWorldLocal::BoundsInAreas( const idBounds &bounds, int *areas, int maxAreas ) const { 1055 int numAreas = 0; 1056 1057 assert( areas ); 1058 assert( bounds[0][0] <= bounds[1][0] && bounds[0][1] <= bounds[1][1] && bounds[0][2] <= bounds[1][2] ); 1059 assert( bounds[1][0] - bounds[0][0] < 1e4f && bounds[1][1] - bounds[0][1] < 1e4f && bounds[1][2] - bounds[0][2] < 1e4f ); 1060 1061 if ( !areaNodes ) { 1062 return numAreas; 1063 } 1064 BoundsInAreas_r( 0, bounds, areas, &numAreas, maxAreas ); 1065 return numAreas; 1066 } 1067 1068 /* 1069 ================ 1070 idRenderWorldLocal::GuiTrace 1071 1072 checks a ray trace against any gui surfaces in an entity, returning the 1073 fraction location of the trace on the gui surface, or -1,-1 if no hit. 1074 this doesn't do any occlusion testing, simply ignoring non-gui surfaces. 1075 start / end are in global world coordinates. 1076 ================ 1077 */ 1078 guiPoint_t idRenderWorldLocal::GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const { 1079 guiPoint_t pt; 1080 pt.x = pt.y = -1; 1081 pt.guiId = 0; 1082 1083 if ( ( entityHandle < 0 ) || ( entityHandle >= entityDefs.Num() ) ) { 1084 common->Printf( "idRenderWorld::GuiTrace: invalid handle %i\n", entityHandle ); 1085 return pt; 1086 } 1087 1088 idRenderEntityLocal * def = entityDefs[entityHandle]; 1089 if ( def == NULL ) { 1090 common->Printf( "idRenderWorld::GuiTrace: handle %i is NULL\n", entityHandle ); 1091 return pt; 1092 } 1093 1094 idRenderModel * model = def->parms.hModel; 1095 if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback != NULL ) { 1096 return pt; 1097 } 1098 1099 // transform the points into local space 1100 idVec3 localStart, localEnd; 1101 R_GlobalPointToLocal( def->modelMatrix, start, localStart ); 1102 R_GlobalPointToLocal( def->modelMatrix, end, localEnd ); 1103 1104 for ( int i = 0; i < model->NumSurfaces(); i++ ) { 1105 const modelSurface_t *surf = model->Surface( i ); 1106 1107 const srfTriangles_t * tri = surf->geometry; 1108 if ( tri == NULL ) { 1109 continue; 1110 } 1111 1112 const idMaterial * shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader ); 1113 if ( shader == NULL ) { 1114 continue; 1115 } 1116 // only trace against gui surfaces 1117 if ( !shader->HasGui() ) { 1118 continue; 1119 } 1120 1121 localTrace_t local = R_LocalTrace( localStart, localEnd, 0.0f, tri ); 1122 if ( local.fraction < 1.0f ) { 1123 idVec3 origin, axis[3]; 1124 1125 R_SurfaceToTextureAxis( tri, origin, axis ); 1126 const idVec3 cursor = local.point - origin; 1127 1128 float axisLen[2]; 1129 axisLen[0] = axis[0].Length(); 1130 axisLen[1] = axis[1].Length(); 1131 1132 pt.x = ( cursor * axis[0] ) / ( axisLen[0] * axisLen[0] ); 1133 pt.y = ( cursor * axis[1] ) / ( axisLen[1] * axisLen[1] ); 1134 pt.guiId = shader->GetEntityGui(); 1135 1136 return pt; 1137 } 1138 } 1139 1140 return pt; 1141 } 1142 1143 /* 1144 =================== 1145 idRenderWorldLocal::ModelTrace 1146 =================== 1147 */ 1148 bool idRenderWorldLocal::ModelTrace( modelTrace_t &trace, qhandle_t entityHandle, const idVec3 &start, const idVec3 &end, const float radius ) const { 1149 1150 memset( &trace, 0, sizeof( trace ) ); 1151 trace.fraction = 1.0f; 1152 trace.point = end; 1153 1154 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) { 1155 return false; 1156 } 1157 1158 idRenderEntityLocal *def = entityDefs[entityHandle]; 1159 if ( def == NULL ) { 1160 return false; 1161 } 1162 1163 renderEntity_t *refEnt = &def->parms; 1164 1165 idRenderModel *model = R_EntityDefDynamicModel( def ); 1166 if ( model == NULL ) { 1167 return false; 1168 } 1169 1170 // transform the points into local space 1171 float modelMatrix[16]; 1172 idVec3 localStart; 1173 idVec3 localEnd; 1174 R_AxisToModelMatrix( refEnt->axis, refEnt->origin, modelMatrix ); 1175 R_GlobalPointToLocal( modelMatrix, start, localStart ); 1176 R_GlobalPointToLocal( modelMatrix, end, localEnd ); 1177 1178 // if we have explicit collision surfaces, only collide against them 1179 // (FIXME, should probably have a parm to control this) 1180 bool collisionSurface = false; 1181 for ( int i = 0; i < model->NumBaseSurfaces(); i++ ) { 1182 const modelSurface_t *surf = model->Surface( i ); 1183 1184 const idMaterial *shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader ); 1185 1186 if ( shader->GetSurfaceFlags() & SURF_COLLISION ) { 1187 collisionSurface = true; 1188 break; 1189 } 1190 } 1191 1192 // only use baseSurfaces, not any overlays 1193 for ( int i = 0; i < model->NumBaseSurfaces(); i++ ) { 1194 const modelSurface_t *surf = model->Surface( i ); 1195 1196 const idMaterial *shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader ); 1197 1198 if ( surf->geometry == NULL || shader == NULL ) { 1199 continue; 1200 } 1201 1202 if ( collisionSurface ) { 1203 // only trace vs collision surfaces 1204 if ( ( shader->GetSurfaceFlags() & SURF_COLLISION ) == 0 ) { 1205 continue; 1206 } 1207 } else { 1208 // skip if not drawn or translucent 1209 if ( !shader->IsDrawn() || ( shader->Coverage() != MC_OPAQUE && shader->Coverage() != MC_PERFORATED ) ) { 1210 continue; 1211 } 1212 } 1213 1214 localTrace_t localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry ); 1215 1216 if ( localTrace.fraction < trace.fraction ) { 1217 trace.fraction = localTrace.fraction; 1218 R_LocalPointToGlobal( modelMatrix, localTrace.point, trace.point ); 1219 trace.normal = localTrace.normal * refEnt->axis; 1220 trace.material = shader; 1221 trace.entity = &def->parms; 1222 trace.jointNumber = refEnt->hModel->NearestJoint( i, localTrace.indexes[0], localTrace.indexes[1], localTrace.indexes[2] ); 1223 } 1224 } 1225 1226 return ( trace.fraction < 1.0f ); 1227 } 1228 1229 /* 1230 =================== 1231 idRenderWorldLocal::Trace 1232 =================== 1233 */ 1234 // FIXME: _D3XP added those. 1235 const char * playerModelExcludeList[] = { 1236 "models/md5/characters/player/d3xp_spplayer.md5mesh", 1237 "models/md5/characters/player/head/d3xp_head.md5mesh", 1238 "models/md5/weapons/pistol_world/worldpistol.md5mesh", 1239 NULL 1240 }; 1241 1242 const char * playerMaterialExcludeList[] = { 1243 "muzzlesmokepuff", 1244 NULL 1245 }; 1246 1247 bool idRenderWorldLocal::Trace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, bool skipDynamic, bool skipPlayer /*_D3XP*/ ) const { 1248 trace.fraction = 1.0f; 1249 trace.point = end; 1250 1251 // bounds for the whole trace 1252 idBounds traceBounds; 1253 traceBounds.Clear(); 1254 traceBounds.AddPoint( start ); 1255 traceBounds.AddPoint( end ); 1256 1257 // get the world areas the trace is in 1258 int areas[128]; 1259 int numAreas = BoundsInAreas( traceBounds, areas, 128 ); 1260 1261 int numSurfaces = 0; 1262 1263 // check all areas for models 1264 for ( int i = 0; i < numAreas; i++ ) { 1265 1266 portalArea_t * area = &portalAreas[ areas[i] ]; 1267 1268 // check all models in this area 1269 for ( areaReference_t * ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) { 1270 idRenderEntityLocal * def = ref->entity; 1271 1272 idRenderModel * model = def->parms.hModel; 1273 if ( model == NULL ) { 1274 continue; 1275 } 1276 1277 if ( model->IsDynamicModel() != DM_STATIC ) { 1278 if ( skipDynamic ) { 1279 continue; 1280 } 1281 1282 #if 1 /* _D3XP addition. could use a cleaner approach */ 1283 if ( skipPlayer ) { 1284 bool exclude = false; 1285 for ( int k = 0; playerModelExcludeList[k] != NULL; k++ ) { 1286 if ( idStr::Cmp( model->Name(), playerModelExcludeList[k] ) == 0 ) { 1287 exclude = true; 1288 break; 1289 } 1290 } 1291 if ( exclude ) { 1292 continue; 1293 } 1294 } 1295 #endif 1296 1297 model = R_EntityDefDynamicModel( def ); 1298 if ( !model ) { 1299 continue; // can happen with particle systems, which don't instantiate without a valid view 1300 } 1301 } 1302 1303 idBounds bounds; 1304 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis ); 1305 1306 // if the model bounds do not overlap with the trace bounds 1307 if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) { 1308 continue; 1309 } 1310 1311 // check all model surfaces 1312 for ( int j = 0; j < model->NumSurfaces(); j++ ) { 1313 const modelSurface_t *surf = model->Surface( j ); 1314 1315 const idMaterial * shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader ); 1316 1317 // if no geometry or no shader 1318 if ( surf->geometry == NULL || shader == NULL ) { 1319 continue; 1320 } 1321 1322 #if 1 /* _D3XP addition. could use a cleaner approach */ 1323 if ( skipPlayer ) { 1324 bool exclude = false; 1325 for ( int k = 0; playerMaterialExcludeList[k] != NULL; k++ ) { 1326 if ( idStr::Cmp( shader->GetName(), playerMaterialExcludeList[k] ) == 0 ) { 1327 exclude = true; 1328 break; 1329 } 1330 } 1331 if ( exclude ) { 1332 continue; 1333 } 1334 } 1335 #endif 1336 1337 const srfTriangles_t * tri = surf->geometry; 1338 1339 bounds.FromTransformedBounds( tri->bounds, def->parms.origin, def->parms.axis ); 1340 1341 // if triangle bounds do not overlap with the trace bounds 1342 if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) { 1343 continue; 1344 } 1345 1346 numSurfaces++; 1347 1348 // transform the points into local space 1349 float modelMatrix[16]; 1350 idVec3 localStart, localEnd; 1351 R_AxisToModelMatrix( def->parms.axis, def->parms.origin, modelMatrix ); 1352 R_GlobalPointToLocal( modelMatrix, start, localStart ); 1353 R_GlobalPointToLocal( modelMatrix, end, localEnd ); 1354 1355 localTrace_t localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry ); 1356 1357 if ( localTrace.fraction < trace.fraction ) { 1358 trace.fraction = localTrace.fraction; 1359 R_LocalPointToGlobal( modelMatrix, localTrace.point, trace.point ); 1360 trace.normal = localTrace.normal * def->parms.axis; 1361 trace.material = shader; 1362 trace.entity = &def->parms; 1363 trace.jointNumber = model->NearestJoint( j, localTrace.indexes[0], localTrace.indexes[1], localTrace.indexes[2] ); 1364 1365 traceBounds.Clear(); 1366 traceBounds.AddPoint( start ); 1367 traceBounds.AddPoint( start + trace.fraction * (end - start) ); 1368 } 1369 } 1370 } 1371 } 1372 return ( trace.fraction < 1.0f ); 1373 } 1374 1375 /* 1376 ================== 1377 idRenderWorldLocal::RecurseProcBSP 1378 ================== 1379 */ 1380 void idRenderWorldLocal::RecurseProcBSP_r( modelTrace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ) const { 1381 float t1, t2; 1382 float frac; 1383 idVec3 mid; 1384 int side; 1385 float midf; 1386 areaNode_t *node; 1387 1388 if ( results->fraction <= p1f) { 1389 return; // already hit something nearer 1390 } 1391 // empty leaf 1392 if ( nodeNum < 0 ) { 1393 return; 1394 } 1395 // if solid leaf node 1396 if ( nodeNum == 0 ) { 1397 if ( parentNodeNum != -1 ) { 1398 1399 results->fraction = p1f; 1400 results->point = p1; 1401 node = &areaNodes[parentNodeNum]; 1402 results->normal = node->plane.Normal(); 1403 return; 1404 } 1405 } 1406 node = &areaNodes[nodeNum]; 1407 1408 // distance from plane for trace start and end 1409 t1 = node->plane.Normal() * p1 + node->plane[3]; 1410 t2 = node->plane.Normal() * p2 + node->plane[3]; 1411 1412 if ( t1 >= 0.0f && t2 >= 0.0f ) { 1413 RecurseProcBSP_r( results, nodeNum, node->children[0], p1f, p2f, p1, p2 ); 1414 return; 1415 } 1416 if ( t1 < 0.0f && t2 < 0.0f ) { 1417 RecurseProcBSP_r( results, nodeNum, node->children[1], p1f, p2f, p1, p2 ); 1418 return; 1419 } 1420 side = t1 < t2; 1421 frac = t1 / (t1 - t2); 1422 midf = p1f + frac*(p2f - p1f); 1423 mid[0] = p1[0] + frac*(p2[0] - p1[0]); 1424 mid[1] = p1[1] + frac*(p2[1] - p1[1]); 1425 mid[2] = p1[2] + frac*(p2[2] - p1[2]); 1426 RecurseProcBSP_r( results, nodeNum, node->children[side], p1f, midf, p1, mid ); 1427 RecurseProcBSP_r( results, nodeNum, node->children[side^1], midf, p2f, mid, p2 ); 1428 } 1429 1430 /* 1431 ================== 1432 idRenderWorldLocal::FastWorldTrace 1433 ================== 1434 */ 1435 bool idRenderWorldLocal::FastWorldTrace( modelTrace_t &results, const idVec3 &start, const idVec3 &end ) const { 1436 memset( &results, 0, sizeof( modelTrace_t ) ); 1437 results.fraction = 1.0f; 1438 if ( areaNodes != NULL ) { 1439 RecurseProcBSP_r( &results, -1, 0, 0.0f, 1.0f, start, end ); 1440 return ( results.fraction < 1.0f ); 1441 } 1442 return false; 1443 } 1444 1445 /* 1446 ================================================================================= 1447 1448 CREATE MODEL REFS 1449 1450 ================================================================================= 1451 */ 1452 1453 /* 1454 ================= 1455 idRenderWorldLocal::AddEntityRefToArea 1456 1457 This is called by R_PushVolumeIntoTree and also directly 1458 for the world model references that are precalculated. 1459 ================= 1460 */ 1461 void idRenderWorldLocal::AddEntityRefToArea( idRenderEntityLocal *def, portalArea_t *area ) { 1462 areaReference_t *ref; 1463 1464 if ( def == NULL ) { 1465 common->Error( "idRenderWorldLocal::AddEntityRefToArea: NULL def" ); 1466 return; 1467 } 1468 1469 for ( ref = def->entityRefs; ref != NULL; ref = ref->ownerNext ) { 1470 if ( ref->area == area ) { 1471 return; 1472 } 1473 } 1474 1475 ref = areaReferenceAllocator.Alloc(); 1476 1477 tr.pc.c_entityReferences++; 1478 1479 ref->entity = def; 1480 1481 // link to entityDef 1482 ref->ownerNext = def->entityRefs; 1483 def->entityRefs = ref; 1484 1485 // link to end of area list 1486 ref->area = area; 1487 ref->areaNext = &area->entityRefs; 1488 ref->areaPrev = area->entityRefs.areaPrev; 1489 ref->areaNext->areaPrev = ref; 1490 ref->areaPrev->areaNext = ref; 1491 } 1492 1493 /* 1494 =================== 1495 idRenderWorldLocal::AddLightRefToArea 1496 =================== 1497 */ 1498 void idRenderWorldLocal::AddLightRefToArea( idRenderLightLocal *light, portalArea_t *area ) { 1499 areaReference_t *lref; 1500 1501 for ( lref = light->references; lref != NULL; lref = lref->ownerNext ) { 1502 if ( lref->area == area ) { 1503 return; 1504 } 1505 } 1506 1507 // add a lightref to this area 1508 lref = areaReferenceAllocator.Alloc(); 1509 lref->light = light; 1510 lref->area = area; 1511 lref->ownerNext = light->references; 1512 light->references = lref; 1513 tr.pc.c_lightReferences++; 1514 1515 // doubly linked list so we can free them easily later 1516 area->lightRefs.areaNext->areaPrev = lref; 1517 lref->areaNext = area->lightRefs.areaNext; 1518 lref->areaPrev = &area->lightRefs; 1519 area->lightRefs.areaNext = lref; 1520 } 1521 1522 /* 1523 =================== 1524 idRenderWorldLocal::GenerateAllInteractions 1525 1526 Force the generation of all light / surface interactions at the start of a level 1527 If this isn't called, they will all be dynamically generated 1528 =================== 1529 */ 1530 void idRenderWorldLocal::GenerateAllInteractions() { 1531 if ( !R_IsInitialized() ) { 1532 return; 1533 } 1534 1535 int start = Sys_Milliseconds(); 1536 1537 generateAllInteractionsCalled = false; 1538 1539 // let the interaction creation code know that it shouldn't 1540 // try and do any view specific optimizations 1541 tr.viewDef = NULL; 1542 1543 // build the interaction table 1544 // this will be dynamically resized if the entity / light counts grow too much 1545 interactionTableWidth = entityDefs.Num() + 100; 1546 interactionTableHeight = lightDefs.Num() + 100; 1547 int size = interactionTableWidth * interactionTableHeight * sizeof( *interactionTable ); 1548 interactionTable = (idInteraction **)R_ClearedStaticAlloc( size ); 1549 1550 // itterate through all lights 1551 int count = 0; 1552 for ( int i = 0; i < this->lightDefs.Num(); i++ ) { 1553 idRenderLightLocal *ldef = this->lightDefs[i]; 1554 if ( ldef == NULL ) { 1555 continue; 1556 } 1557 1558 // check all areas the light touches 1559 for ( areaReference_t *lref = ldef->references; lref; lref = lref->ownerNext ) { 1560 portalArea_t *area = lref->area; 1561 1562 // check all the models in this area 1563 for ( areaReference_t *eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext ) { 1564 idRenderEntityLocal *edef = eref->entity; 1565 1566 // scan the doubly linked lists, which may have several dozen entries 1567 idInteraction *inter; 1568 1569 // we could check either model refs or light refs for matches, but it is 1570 // assumed that there will be less lights in an area than models 1571 // so the entity chains should be somewhat shorter (they tend to be fairly close). 1572 for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) { 1573 if ( inter->lightDef == ldef ) { 1574 break; 1575 } 1576 } 1577 1578 // if we already have an interaction, we don't need to do anything 1579 if ( inter != NULL ) { 1580 continue; 1581 } 1582 1583 // make an interaction for this light / entity pair 1584 // and add a pointer to it in the table 1585 inter = idInteraction::AllocAndLink( edef, ldef ); 1586 count++; 1587 1588 // the interaction may create geometry 1589 inter->CreateStaticInteraction(); 1590 } 1591 } 1592 1593 session->Pump(); 1594 } 1595 1596 int end = Sys_Milliseconds(); 1597 int msec = end - start; 1598 1599 common->Printf( "idRenderWorld::GenerateAllInteractions, msec = %i\n", msec ); 1600 common->Printf( "interactionTable size: %i bytes\n", size ); 1601 common->Printf( "%i interactions take %i bytes\n", count, count * sizeof( idInteraction ) ); 1602 1603 // entities flagged as noDynamicInteractions will no longer make any 1604 generateAllInteractionsCalled = true; 1605 } 1606 1607 /* 1608 =================== 1609 idRenderWorldLocal::FreeInteractions 1610 =================== 1611 */ 1612 void idRenderWorldLocal::FreeInteractions() { 1613 int i; 1614 idRenderEntityLocal *def; 1615 1616 for ( i = 0; i < entityDefs.Num(); i++ ) { 1617 def = entityDefs[i]; 1618 if ( !def ) { 1619 continue; 1620 } 1621 // free all the interactions 1622 while ( def->firstInteraction != NULL ) { 1623 def->firstInteraction->UnlinkAndFree(); 1624 } 1625 } 1626 } 1627 1628 /* 1629 ================== 1630 idRenderWorldLocal::PushFrustumIntoTree_r 1631 1632 Used for both light volumes and model volumes. 1633 1634 This does not clip the points by the planes, so some slop 1635 occurs. 1636 1637 tr.viewCount should be bumped before calling, allowing it 1638 to prevent double checking areas. 1639 1640 We might alternatively choose to do this with an area flow. 1641 ================== 1642 */ 1643 void idRenderWorldLocal::PushFrustumIntoTree_r( idRenderEntityLocal *def, idRenderLightLocal *light, 1644 const frustumCorners_t & corners, int nodeNum ) { 1645 if ( nodeNum < 0 ) { 1646 int areaNum = -1 - nodeNum; 1647 portalArea_t * area = &portalAreas[ areaNum ]; 1648 if ( area->viewCount == tr.viewCount ) { 1649 return; // already added a reference here 1650 } 1651 area->viewCount = tr.viewCount; 1652 1653 if ( def != NULL ) { 1654 AddEntityRefToArea( def, area ); 1655 } 1656 if ( light != NULL ) { 1657 AddLightRefToArea( light, area ); 1658 } 1659 1660 return; 1661 } 1662 1663 areaNode_t * node = areaNodes + nodeNum; 1664 1665 // if we know that all possible children nodes only touch an area 1666 // we have already marked, we can early out 1667 if ( node->commonChildrenArea != CHILDREN_HAVE_MULTIPLE_AREAS && r_useNodeCommonChildren.GetBool() ) { 1668 // note that we do NOT try to set a reference in this area 1669 // yet, because the test volume may yet wind up being in the 1670 // solid part, which would cause bounds slightly poked into 1671 // a wall to show up in the next room 1672 if ( portalAreas[ node->commonChildrenArea ].viewCount == tr.viewCount ) { 1673 return; 1674 } 1675 } 1676 1677 // exact check all the corners against the node plane 1678 frustumCull_t cull = idRenderMatrix::CullFrustumCornersToPlane( corners, node->plane ); 1679 1680 if ( cull != FRUSTUM_CULL_BACK ) { 1681 nodeNum = node->children[0]; 1682 if ( nodeNum != 0 ) { // 0 = solid 1683 PushFrustumIntoTree_r( def, light, corners, nodeNum ); 1684 } 1685 } 1686 1687 if ( cull != FRUSTUM_CULL_FRONT ) { 1688 nodeNum = node->children[1]; 1689 if ( nodeNum != 0 ) { // 0 = solid 1690 PushFrustumIntoTree_r( def, light, corners, nodeNum ); 1691 } 1692 } 1693 } 1694 1695 /* 1696 ============== 1697 idRenderWorldLocal::PushFrustumIntoTree 1698 ============== 1699 */ 1700 void idRenderWorldLocal::PushFrustumIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light, const idRenderMatrix & frustumTransform, const idBounds & frustumBounds ) { 1701 if ( areaNodes == NULL ) { 1702 return; 1703 } 1704 1705 // calculate the corners of the frustum in word space 1706 ALIGNTYPE16 frustumCorners_t corners; 1707 idRenderMatrix::GetFrustumCorners( corners, frustumTransform, frustumBounds ); 1708 1709 PushFrustumIntoTree_r( def, light, corners, 0 ); 1710 } 1711 1712 //=================================================================== 1713 1714 /* 1715 ==================== 1716 idRenderWorldLocal::DebugClearLines 1717 ==================== 1718 */ 1719 void idRenderWorldLocal::DebugClearLines( int time ) { 1720 RB_ClearDebugLines( time ); 1721 RB_ClearDebugText( time ); 1722 } 1723 1724 /* 1725 ==================== 1726 idRenderWorldLocal::DebugLine 1727 ==================== 1728 */ 1729 void idRenderWorldLocal::DebugLine( const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime, const bool depthTest ) { 1730 RB_AddDebugLine( color, start, end, lifetime, depthTest ); 1731 } 1732 1733 /* 1734 ================ 1735 idRenderWorldLocal::DebugArrow 1736 ================ 1737 */ 1738 void idRenderWorldLocal::DebugArrow( const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime ) { 1739 idVec3 forward, right, up, v1, v2; 1740 float a, s; 1741 int i; 1742 static float arrowCos[40]; 1743 static float arrowSin[40]; 1744 static int arrowStep; 1745 1746 DebugLine( color, start, end, lifetime ); 1747 1748 if ( r_debugArrowStep.GetInteger() <= 10 ) { 1749 return; 1750 } 1751 // calculate sine and cosine when step size changes 1752 if ( arrowStep != r_debugArrowStep.GetInteger() ) { 1753 arrowStep = r_debugArrowStep.GetInteger(); 1754 for ( i = 0, a = 0; a < 360.0f; a += arrowStep, i++ ) { 1755 arrowCos[i] = idMath::Cos16( DEG2RAD( a ) ); 1756 arrowSin[i] = idMath::Sin16( DEG2RAD( a ) ); 1757 } 1758 arrowCos[i] = arrowCos[0]; 1759 arrowSin[i] = arrowSin[0]; 1760 } 1761 // draw a nice arrow 1762 forward = end - start; 1763 forward.Normalize(); 1764 forward.NormalVectors( right, up); 1765 for ( i = 0, a = 0; a < 360.0f; a += arrowStep, i++ ) { 1766 s = 0.5f * size * arrowCos[i]; 1767 v1 = end - size * forward; 1768 v1 = v1 + s * right; 1769 s = 0.5f * size * arrowSin[i]; 1770 v1 = v1 + s * up; 1771 1772 s = 0.5f * size * arrowCos[i+1]; 1773 v2 = end - size * forward; 1774 v2 = v2 + s * right; 1775 s = 0.5f * size * arrowSin[i+1]; 1776 v2 = v2 + s * up; 1777 1778 DebugLine( color, v1, end, lifetime ); 1779 DebugLine( color, v1, v2, lifetime ); 1780 } 1781 } 1782 1783 /* 1784 ==================== 1785 idRenderWorldLocal::DebugWinding 1786 ==================== 1787 */ 1788 void idRenderWorldLocal::DebugWinding( const idVec4 &color, const idWinding &w, const idVec3 &origin, const idMat3 &axis, const int lifetime, const bool depthTest ) { 1789 int i; 1790 idVec3 point, lastPoint; 1791 1792 if ( w.GetNumPoints() < 2 ) { 1793 return; 1794 } 1795 1796 lastPoint = origin + w[w.GetNumPoints()-1].ToVec3() * axis; 1797 for ( i = 0; i < w.GetNumPoints(); i++ ) { 1798 point = origin + w[i].ToVec3() * axis; 1799 DebugLine( color, lastPoint, point, lifetime, depthTest ); 1800 lastPoint = point; 1801 } 1802 } 1803 1804 /* 1805 ==================== 1806 idRenderWorldLocal::DebugCircle 1807 ==================== 1808 */ 1809 void idRenderWorldLocal::DebugCircle( const idVec4 &color, const idVec3 &origin, const idVec3 &dir, const float radius, const int numSteps, const int lifetime, const bool depthTest ) { 1810 int i; 1811 float a; 1812 idVec3 left, up, point, lastPoint; 1813 1814 dir.OrthogonalBasis( left, up ); 1815 left *= radius; 1816 up *= radius; 1817 lastPoint = origin + up; 1818 for ( i = 1; i <= numSteps; i++ ) { 1819 a = idMath::TWO_PI * i / numSteps; 1820 point = origin + idMath::Sin16( a ) * left + idMath::Cos16( a ) * up; 1821 DebugLine( color, lastPoint, point, lifetime, depthTest ); 1822 lastPoint = point; 1823 } 1824 } 1825 1826 /* 1827 ============ 1828 idRenderWorldLocal::DebugSphere 1829 ============ 1830 */ 1831 void idRenderWorldLocal::DebugSphere( const idVec4 &color, const idSphere &sphere, const int lifetime, const bool depthTest /*_D3XP*/ ) { 1832 int i, j, n, num; 1833 float s, c; 1834 idVec3 p, lastp, *lastArray; 1835 1836 num = 360 / 15; 1837 lastArray = (idVec3 *) _alloca16( num * sizeof( idVec3 ) ); 1838 lastArray[0] = sphere.GetOrigin() + idVec3( 0, 0, sphere.GetRadius() ); 1839 for ( n = 1; n < num; n++ ) { 1840 lastArray[n] = lastArray[0]; 1841 } 1842 1843 for ( i = 15; i <= 360; i += 15 ) { 1844 s = idMath::Sin16( DEG2RAD(i) ); 1845 c = idMath::Cos16( DEG2RAD(i) ); 1846 lastp[0] = sphere.GetOrigin()[0]; 1847 lastp[1] = sphere.GetOrigin()[1] + sphere.GetRadius() * s; 1848 lastp[2] = sphere.GetOrigin()[2] + sphere.GetRadius() * c; 1849 for ( n = 0, j = 15; j <= 360; j += 15, n++ ) { 1850 p[0] = sphere.GetOrigin()[0] + idMath::Sin16( DEG2RAD(j) ) * sphere.GetRadius() * s; 1851 p[1] = sphere.GetOrigin()[1] + idMath::Cos16( DEG2RAD(j) ) * sphere.GetRadius() * s; 1852 p[2] = lastp[2]; 1853 1854 DebugLine( color, lastp, p, lifetime,depthTest ); 1855 DebugLine( color, lastp, lastArray[n], lifetime, depthTest ); 1856 1857 lastArray[n] = lastp; 1858 lastp = p; 1859 } 1860 } 1861 } 1862 1863 /* 1864 ==================== 1865 idRenderWorldLocal::DebugBounds 1866 ==================== 1867 */ 1868 void idRenderWorldLocal::DebugBounds( const idVec4 &color, const idBounds &bounds, const idVec3 &org, const int lifetime ) { 1869 int i; 1870 idVec3 v[8]; 1871 1872 if ( bounds.IsCleared() ) { 1873 return; 1874 } 1875 1876 for ( i = 0; i < 8; i++ ) { 1877 v[i][0] = org[0] + bounds[(i^(i>>1))&1][0]; 1878 v[i][1] = org[1] + bounds[(i>>1)&1][1]; 1879 v[i][2] = org[2] + bounds[(i>>2)&1][2]; 1880 } 1881 for ( i = 0; i < 4; i++ ) { 1882 DebugLine( color, v[i], v[(i+1)&3], lifetime ); 1883 DebugLine( color, v[4+i], v[4+((i+1)&3)], lifetime ); 1884 DebugLine( color, v[i], v[4+i], lifetime ); 1885 } 1886 } 1887 1888 /* 1889 ==================== 1890 idRenderWorldLocal::DebugBox 1891 ==================== 1892 */ 1893 void idRenderWorldLocal::DebugBox( const idVec4 &color, const idBox &box, const int lifetime ) { 1894 int i; 1895 idVec3 v[8]; 1896 1897 box.ToPoints( v ); 1898 for ( i = 0; i < 4; i++ ) { 1899 DebugLine( color, v[i], v[(i+1)&3], lifetime ); 1900 DebugLine( color, v[4+i], v[4+((i+1)&3)], lifetime ); 1901 DebugLine( color, v[i], v[4+i], lifetime ); 1902 } 1903 } 1904 1905 /* 1906 ============ 1907 idRenderWorldLocal::DebugCone 1908 1909 dir is the cone axis 1910 radius1 is the radius at the apex 1911 radius2 is the radius at apex+dir 1912 ============ 1913 */ 1914 void idRenderWorldLocal::DebugCone( const idVec4 &color, const idVec3 &apex, const idVec3 &dir, float radius1, float radius2, const int lifetime ) { 1915 int i; 1916 idMat3 axis; 1917 idVec3 top, p1, p2, lastp1, lastp2, d; 1918 1919 axis[2] = dir; 1920 axis[2].Normalize(); 1921 axis[2].NormalVectors( axis[0], axis[1] ); 1922 axis[1] = -axis[1]; 1923 1924 top = apex + dir; 1925 lastp2 = top + radius2 * axis[1]; 1926 1927 if ( radius1 == 0.0f ) { 1928 for ( i = 20; i <= 360; i += 20 ) { 1929 d = idMath::Sin16( DEG2RAD(i) ) * axis[0] + idMath::Cos16( DEG2RAD(i) ) * axis[1]; 1930 p2 = top + d * radius2; 1931 DebugLine( color, lastp2, p2, lifetime ); 1932 DebugLine( color, p2, apex, lifetime ); 1933 lastp2 = p2; 1934 } 1935 } else { 1936 lastp1 = apex + radius1 * axis[1]; 1937 for ( i = 20; i <= 360; i += 20 ) { 1938 d = idMath::Sin16( DEG2RAD(i) ) * axis[0] + idMath::Cos16( DEG2RAD(i) ) * axis[1]; 1939 p1 = apex + d * radius1; 1940 p2 = top + d * radius2; 1941 DebugLine( color, lastp1, p1, lifetime ); 1942 DebugLine( color, lastp2, p2, lifetime ); 1943 DebugLine( color, p1, p2, lifetime ); 1944 lastp1 = p1; 1945 lastp2 = p2; 1946 } 1947 } 1948 } 1949 1950 /* 1951 ================ 1952 idRenderWorldLocal::DebugAxis 1953 ================ 1954 */ 1955 void idRenderWorldLocal::DebugAxis( const idVec3 &origin, const idMat3 &axis ) { 1956 idVec3 start = origin; 1957 idVec3 end = start + axis[0] * 20.0f; 1958 DebugArrow( colorWhite, start, end, 2 ); 1959 end = start + axis[0] * -20.0f; 1960 DebugArrow( colorWhite, start, end, 2 ); 1961 end = start + axis[1] * +20.0f; 1962 DebugArrow( colorGreen, start, end, 2 ); 1963 end = start + axis[1] * -20.0f; 1964 DebugArrow( colorGreen, start, end, 2 ); 1965 end = start + axis[2] * +20.0f; 1966 DebugArrow( colorBlue, start, end, 2 ); 1967 end = start + axis[2] * -20.0f; 1968 DebugArrow( colorBlue, start, end, 2 ); 1969 } 1970 1971 /* 1972 ==================== 1973 idRenderWorldLocal::DebugClearPolygons 1974 ==================== 1975 */ 1976 void idRenderWorldLocal::DebugClearPolygons( int time ) { 1977 RB_ClearDebugPolygons( time ); 1978 } 1979 1980 /* 1981 ==================== 1982 idRenderWorldLocal::DebugPolygon 1983 ==================== 1984 */ 1985 void idRenderWorldLocal::DebugPolygon( const idVec4 &color, const idWinding &winding, const int lifeTime, const bool depthTest ) { 1986 RB_AddDebugPolygon( color, winding, lifeTime, depthTest ); 1987 } 1988 1989 /* 1990 ================ 1991 idRenderWorldLocal::DebugScreenRect 1992 ================ 1993 */ 1994 void idRenderWorldLocal::DebugScreenRect( const idVec4 &color, const idScreenRect &rect, const viewDef_t *viewDef, const int lifetime ) { 1995 int i; 1996 float centerx, centery, dScale, hScale, vScale; 1997 idBounds bounds; 1998 idVec3 p[4]; 1999 2000 centerx = ( viewDef->viewport.x2 - viewDef->viewport.x1 ) * 0.5f; 2001 centery = ( viewDef->viewport.y2 - viewDef->viewport.y1 ) * 0.5f; 2002 2003 dScale = r_znear.GetFloat() + 1.0f; 2004 hScale = dScale * idMath::Tan16( DEG2RAD( viewDef->renderView.fov_x * 0.5f ) ); 2005 vScale = dScale * idMath::Tan16( DEG2RAD( viewDef->renderView.fov_y * 0.5f ) ); 2006 2007 bounds[0][0] = bounds[1][0] = dScale; 2008 bounds[0][1] = -( rect.x1 - centerx ) / centerx * hScale; 2009 bounds[1][1] = -( rect.x2 - centerx ) / centerx * hScale; 2010 bounds[0][2] = ( rect.y1 - centery ) / centery * vScale; 2011 bounds[1][2] = ( rect.y2 - centery ) / centery * vScale; 2012 2013 for ( i = 0; i < 4; i++ ) { 2014 p[i].x = bounds[0][0]; 2015 p[i].y = bounds[(i^(i>>1))&1].y; 2016 p[i].z = bounds[(i>>1)&1].z; 2017 p[i] = viewDef->renderView.vieworg + p[i] * viewDef->renderView.viewaxis; 2018 } 2019 for ( i = 0; i < 4; i++ ) { 2020 DebugLine( color, p[i], p[(i+1)&3], false ); 2021 } 2022 } 2023 2024 /* 2025 ================ 2026 idRenderWorldLocal::DrawTextLength 2027 2028 returns the length of the given text 2029 ================ 2030 */ 2031 float idRenderWorldLocal::DrawTextLength( const char *text, float scale, int len ) { 2032 return RB_DrawTextLength( text, scale, len ); 2033 } 2034 2035 /* 2036 ================ 2037 idRenderWorldLocal::DrawText 2038 2039 oriented on the viewaxis 2040 align can be 0-left, 1-center (default), 2-right 2041 ================ 2042 */ 2043 void idRenderWorldLocal::DrawText( const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align, const int lifetime, const bool depthTest ) { 2044 RB_AddDebugText( text, origin, scale, color, viewAxis, align, lifetime, depthTest ); 2045 } 2046 2047 /* 2048 =============== 2049 idRenderWorldLocal::RegenerateWorld 2050 =============== 2051 */ 2052 void idRenderWorldLocal::RegenerateWorld() { 2053 R_FreeDerivedData(); 2054 R_ReCreateWorldReferences(); 2055 } 2056 2057 /* 2058 =============== 2059 R_GlobalShaderOverride 2060 =============== 2061 */ 2062 bool R_GlobalShaderOverride( const idMaterial **shader ) { 2063 2064 if ( !(*shader)->IsDrawn() ) { 2065 return false; 2066 } 2067 2068 if ( tr.primaryRenderView.globalMaterial ) { 2069 *shader = tr.primaryRenderView.globalMaterial; 2070 return true; 2071 } 2072 2073 if ( r_materialOverride.GetString()[0] != '\0' ) { 2074 *shader = declManager->FindMaterial( r_materialOverride.GetString() ); 2075 return true; 2076 } 2077 2078 return false; 2079 } 2080 2081 /* 2082 =============== 2083 R_RemapShaderBySkin 2084 =============== 2085 */ 2086 const idMaterial *R_RemapShaderBySkin( const idMaterial *shader, const idDeclSkin *skin, const idMaterial *customShader ) { 2087 2088 if ( !shader ) { 2089 return NULL; 2090 } 2091 2092 // never remap surfaces that were originally nodraw, like collision hulls 2093 if ( !shader->IsDrawn() ) { 2094 return shader; 2095 } 2096 2097 if ( customShader ) { 2098 // this is sort of a hack, but cause deformed surfaces to map to empty surfaces, 2099 // so the item highlight overlay doesn't highlight the autosprite surface 2100 if ( shader->Deform() ) { 2101 return NULL; 2102 } 2103 return const_cast<idMaterial *>(customShader); 2104 } 2105 2106 if ( !skin ) { 2107 return const_cast<idMaterial *>(shader); 2108 } 2109 2110 return skin->RemapShaderBySkin( shader ); 2111 }