Push.cpp (40396B)
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 "../Game_local.h" 33 34 35 /* 36 ============ 37 idPush::InitSavingPushedEntityPositions 38 ============ 39 */ 40 void idPush::InitSavingPushedEntityPositions() { 41 numPushed = 0; 42 } 43 44 /* 45 ============ 46 idPush::SaveEntityPosition 47 ============ 48 */ 49 void idPush::SaveEntityPosition( idEntity *ent ) { 50 int i; 51 52 // if already saved the physics state for this entity 53 for ( i = 0; i < numPushed; i++ ) { 54 if ( pushed[i].ent == ent ) { 55 return; 56 } 57 } 58 59 // don't overflow 60 if ( numPushed >= MAX_GENTITIES ) { 61 gameLocal.Error( "more than MAX_GENTITIES pushed entities" ); 62 return; 63 } 64 65 pushed[numPushed].ent = ent; 66 67 // if the entity is an actor 68 if ( ent->IsType( idActor::Type ) ) { 69 // save the delta view angles 70 pushed[numPushed].deltaViewAngles = static_cast<idActor *>(ent)->GetDeltaViewAngles(); 71 } 72 73 // save the physics state 74 ent->GetPhysics()->SaveState(); 75 76 numPushed++; 77 } 78 79 /* 80 ============ 81 idPush::RestorePushedEntityPositions 82 ============ 83 */ 84 void idPush::RestorePushedEntityPositions() { 85 int i; 86 87 for ( i = 0; i < numPushed; i++ ) { 88 89 // if the entity is an actor 90 if ( pushed[i].ent->IsType( idActor::Type ) ) { 91 // set back the delta view angles 92 static_cast<idActor *>(pushed[i].ent)->SetDeltaViewAngles( pushed[i].deltaViewAngles ); 93 } 94 95 // restore the physics state 96 pushed[i].ent->GetPhysics()->RestoreState(); 97 } 98 } 99 100 /* 101 ============ 102 idPush::RotateEntityToAxial 103 ============ 104 */ 105 bool idPush::RotateEntityToAxial( idEntity *ent, idVec3 rotationPoint ) { 106 int i; 107 trace_t trace; 108 idRotation rotation; 109 idMat3 axis; 110 idPhysics *physics; 111 112 physics = ent->GetPhysics(); 113 axis = physics->GetAxis(); 114 if ( !axis.IsRotated() ) { 115 return true; 116 } 117 // try to rotate the bbox back to axial with at most four rotations 118 for ( i = 0; i < 4; i++ ) { 119 axis = physics->GetAxis(); 120 rotation = axis.ToRotation(); 121 rotation.Scale( -1 ); 122 rotation.SetOrigin( rotationPoint ); 123 // tiny float numbers in the clip axis, this can get the entity stuck 124 if ( rotation.GetAngle() == 0.0f ) { 125 physics->SetAxis( mat3_identity ); 126 return true; 127 } 128 // 129 ent->GetPhysics()->ClipRotation( trace, rotation, NULL ); 130 // if the full rotation is possible 131 if ( trace.fraction >= 1.0f ) { 132 // set bbox in final axial position 133 physics->SetOrigin( trace.endpos ); 134 physics->SetAxis( mat3_identity ); 135 return true; 136 } 137 // if partial rotation was possible 138 else if ( trace.fraction > 0.0f ) { 139 // partial rotation 140 physics->SetOrigin( trace.endpos ); 141 physics->SetAxis( trace.endAxis ); 142 } 143 // next rotate around collision point 144 rotationPoint = trace.c.point; 145 } 146 return false; 147 } 148 149 #ifdef NEW_PUSH 150 151 /* 152 ============ 153 idPush::CanPushEntity 154 ============ 155 */ 156 bool idPush::CanPushEntity( idEntity *ent, idEntity *pusher, idEntity *initialPusher, const int flags ) { 157 158 // if the physics object is not pushable 159 if ( !ent->GetPhysics()->IsPushable() ) { 160 return false; 161 } 162 163 // if the entity doesn't clip with this pusher 164 if ( !( ent->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) { 165 return false; 166 } 167 168 // don't push players in noclip mode 169 if ( ent->client && ent->client->noclip ) { 170 return false; 171 } 172 173 // if we should only push idMoveable entities 174 if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !ent->IsType( idMoveable::Type ) ) { 175 return false; 176 } 177 178 // if we shouldn't push entities the original pusher rests upon 179 if ( flags & PUSHFL_NOGROUNDENTITIES ) { 180 if ( initialPusher->GetPhysics()->IsGroundEntity( ent->entityNumber ) ) { 181 return false; 182 } 183 } 184 185 return true; 186 } 187 188 /* 189 ============ 190 idPush::AddEntityToPushedGroup 191 ============ 192 */ 193 void idPush::AddEntityToPushedGroup( idEntity *ent, float fraction, bool groundContact ) { 194 int i, j; 195 196 for ( i = 0; i < pushedGroupSize; i++ ) { 197 if ( ent == pushedGroup[i].ent ) { 198 if ( fraction > pushedGroup[i].fraction ) { 199 pushedGroup[i].fraction = fraction; 200 pushedGroup[i].groundContact &= groundContact; 201 pushedGroup[i].test = true; 202 } 203 return; 204 } 205 else if ( fraction > pushedGroup[i].fraction ) { 206 for ( j = pushedGroupSize; j > i; j-- ) { 207 pushedGroup[j] = pushedGroup[j-1]; 208 } 209 break; 210 } 211 } 212 213 // put the entity in the group 214 pushedGroupSize++; 215 pushedGroup[i].ent = ent; 216 pushedGroup[i].fraction = fraction; 217 pushedGroup[i].groundContact = groundContact; 218 pushedGroup[i].test = true; 219 220 // remove any further occurances of the same entity in the group 221 for ( i++; i < pushedGroupSize; i++ ) { 222 if ( ent == pushedGroup[i].ent ) { 223 for ( j = i+1; j < pushedGroupSize; j++ ) { 224 pushedGroup[j-1] = pushedGroup[j]; 225 } 226 pushedGroupSize--; 227 break; 228 } 229 } 230 } 231 232 /* 233 ============ 234 idPush::IsFullyPushed 235 ============ 236 */ 237 bool idPush::IsFullyPushed( idEntity *ent ) { 238 int i; 239 240 for ( i = 0; i < pushedGroupSize; i++ ) { 241 if ( pushedGroup[i].fraction < 1.0f ) { 242 return false; 243 } 244 if ( ent == pushedGroup[i].ent ) { 245 return true; 246 } 247 } 248 return false; 249 } 250 251 /* 252 ============ 253 idPush::ClipTranslationAgainstPusher 254 ============ 255 */ 256 bool idPush::ClipTranslationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idVec3 &translation ) { 257 int i, n; 258 trace_t t; 259 260 results.fraction = 1.0f; 261 262 n = pusher->GetPhysics()->GetNumClipModels(); 263 for ( i = 0; i < n; i++ ) { 264 ent->GetPhysics()->ClipTranslation( t, translation, pusher->GetPhysics()->GetClipModel( i ) ); 265 if ( t.fraction < results.fraction ) { 266 results = t; 267 } 268 } 269 return ( results.fraction < 1.0f ); 270 } 271 272 /* 273 ============ 274 idPush::GetPushableEntitiesForTranslation 275 ============ 276 */ 277 int idPush::GetPushableEntitiesForTranslation( idEntity *pusher, idEntity *initialPusher, const int flags, 278 const idVec3 &translation, idEntity *entityList[], int maxEntities ) { 279 int i, n, l; 280 idBounds bounds, pushBounds; 281 idPhysics *physics; 282 283 // get bounds for the whole movement 284 physics = pusher->GetPhysics(); 285 bounds = physics->GetBounds(); 286 pushBounds.FromBoundsTranslation( bounds, physics->GetOrigin(), physics->GetAxis(), translation ); 287 pushBounds.ExpandSelf( 2.0f ); 288 289 // get all entities within the push bounds 290 n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); 291 292 for ( l = i = 0; i < n; i++ ) { 293 if ( entityList[i] == pusher || entityList[i] == initialPusher ) { 294 continue; 295 } 296 if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) { 297 entityList[l++] = entityList[i]; 298 } 299 } 300 301 return l; 302 } 303 304 /* 305 ============ 306 idPush::ClipTranslationalPush 307 308 Try to push other entities by translating the given entity. 309 ============ 310 */ 311 float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags, 312 const idVec3 &newOrigin, const idVec3 &translation ) { 313 int i, j, numListedEntities; 314 idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ]; 315 float fraction; 316 bool groundContact, blocked = false; 317 float totalMass; 318 trace_t trace; 319 idVec3 realTranslation, partialTranslation; 320 321 totalMass = 0.0f; 322 323 results.fraction = 1.0f; 324 results.endpos = newOrigin; 325 results.endAxis = pusher->GetPhysics()->GetAxis(); 326 memset( results.c, 0, sizeof( results.c ) ); 327 328 if ( translation == vec3_origin ) { 329 return totalMass; 330 } 331 332 // clip against all non-pushable physics objects 333 if ( flags & PUSHFL_CLIP ) { 334 335 numListedEntities = GetPushableEntitiesForTranslation( pusher, pusher, flags, translation, entityList, MAX_GENTITIES ); 336 // disable pushable entities for collision detection 337 for ( i = 0; i < numListedEntities; i++ ) { 338 entityList[i]->GetPhysics()->DisableClip(); 339 } 340 // clip translation 341 pusher->GetPhysics()->ClipTranslation( results, translation, NULL ); 342 // enable pushable entities 343 for ( i = 0; i < numListedEntities; i++ ) { 344 entityList[i]->GetPhysics()->EnableClip(); 345 } 346 if ( results.fraction == 0.0f ) { 347 return totalMass; 348 } 349 realTranslation = results.fraction * translation; 350 } 351 else { 352 realTranslation = translation; 353 } 354 355 // put the pusher in the group of pushed physics objects 356 pushedGroup[0].ent = pusher; 357 pushedGroup[0].fraction = 1.0f; 358 pushedGroup[0].groundContact = true; 359 pushedGroup[0].test = true; 360 pushedGroupSize = 1; 361 362 // get all physics objects that need to be pushed 363 for ( i = 0; i < pushedGroupSize; ) { 364 if ( !pushedGroup[i].test ) { 365 i++; 366 continue; 367 } 368 pushedGroup[i].test = false; 369 curPusher = pushedGroup[i].ent; 370 fraction = pushedGroup[i].fraction; 371 groundContact = pushedGroup[i].groundContact; 372 i = 0; 373 374 numListedEntities = GetPushableEntitiesForTranslation( curPusher, pusher, flags, realTranslation, entityList, MAX_GENTITIES ); 375 376 for ( j = 0; j < numListedEntities; j++ ) { 377 ent = entityList[ j ]; 378 379 if ( IsFullyPushed( ent ) ) { 380 continue; 381 } 382 383 if ( !CanPushEntity( ent, curPusher, pusher, flags ) ) { 384 continue; 385 } 386 387 if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) { 388 AddEntityToPushedGroup( ent, 1.0f * fraction, false ); 389 } 390 else if ( ClipTranslationAgainstPusher( trace, ent, curPusher, -fraction * realTranslation ) ) { 391 AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact ); 392 } 393 } 394 } 395 396 // save physics states and disable physics objects for collision detection 397 for ( i = 0; i < pushedGroupSize; i++ ) { 398 SaveEntityPosition( pushedGroup[i].ent ); 399 pushedGroup[i].ent->GetPhysics()->DisableClip(); 400 } 401 402 // clip all pushed physics objects 403 for ( i = 1; i < pushedGroupSize; i++ ) { 404 partialTranslation = realTranslation * pushedGroup[i].fraction; 405 406 pushedGroup[i].ent->GetPhysics()->ClipTranslation( trace, partialTranslation, NULL ); 407 408 if ( trace.fraction < 1.0f ) { 409 blocked = true; 410 break; 411 } 412 } 413 414 // enable all physics objects for collision detection 415 for ( i = 1; i < pushedGroupSize; i++ ) { 416 pushedGroup[i].ent->GetPhysics()->EnableClip(); 417 } 418 419 // push all or nothing 420 if ( blocked ) { 421 if ( flags & PUSHFL_CLIP ) { 422 pusher->GetPhysics()->ClipTranslation( results, realTranslation, NULL ); 423 } 424 else { 425 results.fraction = 0.0f; 426 results.endpos = pusher->GetPhysics()->GetOrigin(); 427 results.endAxis = pusher->GetPhysics()->GetAxis(); 428 } 429 } 430 else { 431 // translate all pushed physics objects 432 for ( i = 1; i < pushedGroupSize; i++ ) { 433 partialTranslation = realTranslation * pushedGroup[i].fraction; 434 pushedGroup[i].ent->GetPhysics()->Translate( partialTranslation ); 435 totalMass += pushedGroup[i].ent->GetPhysics()->GetMass(); 436 } 437 // translate the clip models of the pusher 438 for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) { 439 pusher->GetPhysics()->GetClipModel(i)->Translate( results.fraction * realTranslation ); 440 pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip ); 441 } 442 } 443 444 return totalMass; 445 } 446 447 /* 448 ============ 449 idPush::ClipRotationAgainstPusher 450 ============ 451 */ 452 bool idPush::ClipRotationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idRotation &rotation ) { 453 int i, n; 454 trace_t t; 455 456 results.fraction = 1.0f; 457 458 n = pusher->GetPhysics()->GetNumClipModels(); 459 for ( i = 0; i < n; i++ ) { 460 ent->GetPhysics()->ClipRotation( t, rotation, pusher->GetPhysics()->GetClipModel( i ) ); 461 if ( t.fraction < results.fraction ) { 462 results = t; 463 } 464 } 465 return ( results.fraction < 1.0f ); 466 } 467 468 /* 469 ============ 470 idPush::GetPushableEntitiesForRotation 471 ============ 472 */ 473 int idPush::GetPushableEntitiesForRotation( idEntity *pusher, idEntity *initialPusher, const int flags, 474 const idRotation &rotation, idEntity *entityList[], int maxEntities ) { 475 int i, n, l; 476 idBounds bounds, pushBounds; 477 idPhysics *physics; 478 479 // get bounds for the whole movement 480 physics = pusher->GetPhysics(); 481 bounds = physics->GetBounds(); 482 pushBounds.FromBoundsRotation( bounds, physics->GetOrigin(), physics->GetAxis(), rotation ); 483 pushBounds.ExpandSelf( 2.0f ); 484 485 // get all entities within the push bounds 486 n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); 487 488 for ( l = i = 0; i < n; i++ ) { 489 if ( entityList[i] == pusher || entityList[i] == initialPusher ) { 490 continue; 491 } 492 if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) { 493 entityList[l++] = entityList[i]; 494 } 495 } 496 497 return l; 498 } 499 500 /* 501 ============ 502 idPush::ClipRotationalPush 503 504 Try to push other entities by rotating the given entity. 505 ============ 506 */ 507 float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags, 508 const idMat3 &newAxis, const idRotation &rotation ) { 509 int i, j, numListedEntities; 510 idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ]; 511 float fraction; 512 bool groundContact, blocked = false; 513 float totalMass; 514 trace_t trace; 515 idRotation realRotation, partialRotation; 516 idMat3 oldAxis; 517 518 totalMass = 0.0f; 519 520 results.fraction = 1.0f; 521 results.endpos = pusher->GetPhysics()->GetOrigin(); 522 results.endAxis = newAxis; 523 memset( results.c, 0, sizeof( results.c ) ); 524 525 if ( !rotation.GetAngle() ) { 526 return totalMass; 527 } 528 529 // clip against all non-pushable physics objects 530 if ( flags & PUSHFL_CLIP ) { 531 532 numListedEntities = GetPushableEntitiesForRotation( pusher, pusher, flags, rotation, entityList, MAX_GENTITIES ); 533 // disable pushable entities for collision detection 534 for ( i = 0; i < numListedEntities; i++ ) { 535 entityList[i]->GetPhysics()->DisableClip(); 536 } 537 // clip rotation 538 pusher->GetPhysics()->ClipRotation( results, rotation, NULL ); 539 // enable pushable entities 540 for ( i = 0; i < numListedEntities; i++ ) { 541 entityList[i]->GetPhysics()->EnableClip(); 542 } 543 if ( results.fraction == 0.0f ) { 544 return totalMass; 545 } 546 realRotation = results.fraction * rotation; 547 } 548 else { 549 realRotation = rotation; 550 } 551 552 // put the pusher in the group of pushed physics objects 553 pushedGroup[0].ent = pusher; 554 pushedGroup[0].fraction = 1.0f; 555 pushedGroup[0].groundContact = true; 556 pushedGroup[0].test = true; 557 pushedGroupSize = 1; 558 559 // get all physics objects that need to be pushed 560 for ( i = 0; i < pushedGroupSize; ) { 561 if ( !pushedGroup[i].test ) { 562 i++; 563 continue; 564 } 565 pushedGroup[i].test = false; 566 curPusher = pushedGroup[i].ent; 567 fraction = pushedGroup[i].fraction; 568 groundContact = pushedGroup[i].groundContact; 569 i = 0; 570 571 numListedEntities = GetPushableEntitiesForRotation( curPusher, pusher, flags, realRotation, entityList, MAX_GENTITIES ); 572 573 for ( j = 0; j < numListedEntities; j++ ) { 574 ent = entityList[ j ]; 575 576 if ( IsFullyPushed( ent ) ) { 577 continue; 578 } 579 580 if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) { 581 AddEntityToPushedGroup( ent, 1.0f * fraction, false ); 582 } 583 else if ( ClipRotationAgainstPusher( trace, ent, curPusher, -fraction * realRotation ) ) { 584 AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact ); 585 } 586 } 587 } 588 589 // save physics states and disable physics objects for collision detection 590 for ( i = 1; i < pushedGroupSize; i++ ) { 591 SaveEntityPosition( pushedGroup[i].ent ); 592 pushedGroup[i].ent->GetPhysics()->DisableClip(); 593 } 594 595 // clip all pushed physics objects 596 for ( i = 1; i < pushedGroupSize; i++ ) { 597 partialRotation = realRotation * pushedGroup[i].fraction; 598 599 pushedGroup[i].ent->GetPhysics()->ClipRotation( trace, partialRotation, NULL ); 600 601 if ( trace.fraction < 1.0f ) { 602 blocked = true; 603 break; 604 } 605 } 606 607 // enable all physics objects for collision detection 608 for ( i = 1; i < pushedGroupSize; i++ ) { 609 pushedGroup[i].ent->GetPhysics()->EnableClip(); 610 } 611 612 // push all or nothing 613 if ( blocked ) { 614 if ( flags & PUSHFL_CLIP ) { 615 pusher->GetPhysics()->ClipRotation( results, realRotation, NULL ); 616 } 617 else { 618 results.fraction = 0.0f; 619 results.endpos = pusher->GetPhysics()->GetOrigin(); 620 results.endAxis = pusher->GetPhysics()->GetAxis(); 621 } 622 } 623 else { 624 // rotate all pushed physics objects 625 for ( i = 1; i < pushedGroupSize; i++ ) { 626 partialRotation = realRotation * pushedGroup[i].fraction; 627 pushedGroup[i].ent->GetPhysics()->Rotate( partialRotation ); 628 totalMass += pushedGroup[i].ent->GetPhysics()->GetMass(); 629 } 630 // rotate the clip models of the pusher 631 for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) { 632 pusher->GetPhysics()->GetClipModel(i)->Rotate( realRotation ); 633 pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip ); 634 pusher->GetPhysics()->GetClipModel(i)->Enable(); 635 } 636 // rotate any actors back to axial 637 for ( i = 1; i < pushedGroupSize; i++ ) { 638 // if the entity is using actor physics 639 if ( pushedGroup[i].ent->GetPhysics()->IsType( idPhysics_Actor::Type ) ) { 640 641 // rotate the collision model back to axial 642 if ( !RotateEntityToAxial( pushedGroup[i].ent, pushedGroup[i].ent->GetPhysics()->GetOrigin() ) ) { 643 // don't allow rotation if the bbox is no longer axial 644 results.fraction = 0.0f; 645 results.endpos = pusher->GetPhysics()->GetOrigin(); 646 results.endAxis = pusher->GetPhysics()->GetAxis(); 647 } 648 } 649 } 650 } 651 652 return totalMass; 653 } 654 655 #else /* !NEW_PUSH */ 656 657 enum { 658 PUSH_NO, // not pushed 659 PUSH_OK, // pushed ok 660 PUSH_BLOCKED // blocked 661 }; 662 663 /* 664 ============ 665 idPush::ClipEntityRotation 666 ============ 667 */ 668 void idPush::ClipEntityRotation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idRotation &rotation ) { 669 670 if ( skip ) { 671 skip->Disable(); 672 } 673 674 ent->GetPhysics()->ClipRotation( trace, rotation, clipModel ); 675 676 if ( skip ) { 677 skip->Enable(); 678 } 679 } 680 681 /* 682 ============ 683 idPush::ClipEntityTranslation 684 ============ 685 */ 686 void idPush::ClipEntityTranslation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idVec3 &translation ) { 687 688 if ( skip ) { 689 skip->Disable(); 690 } 691 692 ent->GetPhysics()->ClipTranslation( trace, translation, clipModel ); 693 694 if ( skip ) { 695 skip->Enable(); 696 } 697 } 698 699 /* 700 ============ 701 idPush::TryRotatePushEntity 702 ============ 703 */ 704 #ifdef _DEBUG 705 // #define ROTATIONAL_PUSH_DEBUG 706 #endif 707 708 int idPush::TryRotatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, 709 const idMat3 &newAxis, const idRotation &rotation ) { 710 trace_t trace; 711 idVec3 rotationPoint; 712 idRotation newRotation; 713 float checkAngle; 714 idPhysics *physics; 715 716 physics = check->GetPhysics(); 717 718 #ifdef ROTATIONAL_PUSH_DEBUG 719 bool startsolid = false; 720 if ( physics->ClipContents( clipModel ) ) { 721 startsolid = true; 722 } 723 #endif 724 725 results.fraction = 1.0f; 726 results.endpos = clipModel->GetOrigin(); 727 results.endAxis = newAxis; 728 memset( &results.c, 0, sizeof( results.c ) ); 729 730 // always pushed when standing on the pusher 731 if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { 732 // rotate the entity colliding with all other entities except the pusher itself 733 ClipEntityRotation( trace, check, NULL, clipModel, rotation ); 734 // if there is a collision 735 if ( trace.fraction < 1.0f ) { 736 // angle along which the entity is pushed 737 checkAngle = rotation.GetAngle() * trace.fraction; 738 // test if the entity can stay at it's partly pushed position by rotating 739 // the entity in reverse only colliding with pusher 740 newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), -(rotation.GetAngle() - checkAngle) ); 741 ClipEntityRotation( results, check, clipModel, NULL, newRotation ); 742 // if there is a collision 743 if ( results.fraction < 1.0f ) { 744 745 // FIXME: try to push the blocking entity as well or try to slide along collision plane(s)? 746 747 results.c.normal = -results.c.normal; 748 results.c.dist = -results.c.dist; 749 750 // the entity will be crushed between the pusher and some other entity 751 return PUSH_BLOCKED; 752 } 753 } 754 else { 755 // angle along which the entity is pushed 756 checkAngle = rotation.GetAngle(); 757 } 758 // point to rotate entity bbox around back to axial 759 rotationPoint = physics->GetOrigin(); 760 } 761 else { 762 // rotate entity in reverse only colliding with pusher 763 newRotation = rotation; 764 newRotation.Scale( -1 ); 765 // 766 ClipEntityRotation( results, check, clipModel, NULL, newRotation ); 767 // if no collision with the pusher then the entity is not pushed by the pusher 768 if ( results.fraction >= 1.0f ) { 769 #ifdef ROTATIONAL_PUSH_DEBUG 770 // set pusher into final position 771 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); 772 if ( physics->ClipContents( clipModel ) ) { 773 if ( !startsolid ) { 774 int bah = 1; 775 } 776 } 777 #endif 778 return PUSH_NO; 779 } 780 // get point to rotate bbox around back to axial 781 rotationPoint = results.c.point; 782 // angle along which the entity will be pushed 783 checkAngle = rotation.GetAngle() * (1.0f - results.fraction); 784 // rotate the entity colliding with all other entities except the pusher itself 785 newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle ); 786 ClipEntityRotation( trace, check, NULL, clipModel, newRotation ); 787 // if there is a collision 788 if ( trace.fraction < 1.0f ) { 789 790 // FIXME: try to push the blocking entity as well or try to slide along collision plane(s)? 791 792 results.c.normal = -results.c.normal; 793 results.c.dist = -results.c.dist; 794 795 // the entity will be crushed between the pusher and some other entity 796 return PUSH_BLOCKED; 797 } 798 } 799 800 SaveEntityPosition( check ); 801 802 newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle ); 803 // NOTE: this code prevents msvc 6.0 & 7.0 from screwing up the above code in 804 // release builds moving less floats than it should 805 static float shit = checkAngle; 806 807 newRotation.RotatePoint( rotationPoint ); 808 809 // rotate the entity 810 physics->Rotate( newRotation ); 811 812 // set pusher into final position 813 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); 814 815 #ifdef ROTATIONAL_PUSH_DEBUG 816 if ( physics->ClipContents( clipModel ) ) { 817 if ( !startsolid ) { 818 int bah = 1; 819 } 820 } 821 #endif 822 823 // if the entity uses actor physics 824 if ( physics->IsType( idPhysics_Actor::Type ) ) { 825 826 // rotate the collision model back to axial 827 if ( !RotateEntityToAxial( check, rotationPoint ) ) { 828 // don't allow rotation if the bbox is no longer axial 829 return PUSH_BLOCKED; 830 } 831 } 832 833 #ifdef ROTATIONAL_PUSH_DEBUG 834 if ( physics->ClipContents( clipModel ) ) { 835 if ( !startsolid ) { 836 int bah = 1; 837 } 838 } 839 #endif 840 841 // if the entity is an actor using actor physics 842 if ( check->IsType( idActor::Type ) && physics->IsType( idPhysics_Actor::Type ) ) { 843 844 // if the entity is standing ontop of the pusher 845 if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { 846 // rotate actor view 847 idActor *actor = static_cast<idActor *>(check); 848 idAngles delta = actor->GetDeltaViewAngles(); 849 delta.yaw += newRotation.ToMat3()[0].ToYaw(); 850 actor->SetDeltaViewAngles( delta ); 851 } 852 } 853 854 return PUSH_OK; 855 } 856 857 /* 858 ============ 859 idPush::TryTranslatePushEntity 860 ============ 861 */ 862 #ifdef _DEBUG 863 // #define TRANSLATIONAL_PUSH_DEBUG 864 #endif 865 866 int idPush::TryTranslatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, 867 const idVec3 &newOrigin, const idVec3 &move ) { 868 trace_t trace; 869 idVec3 checkMove; 870 idVec3 oldOrigin; 871 idPhysics *physics; 872 873 physics = check->GetPhysics(); 874 875 #ifdef TRANSLATIONAL_PUSH_DEBUG 876 bool startsolid = false; 877 if ( physics->ClipContents( clipModel ) ) { 878 startsolid = true; 879 } 880 #endif 881 882 results.fraction = 1.0f; 883 results.endpos = newOrigin; 884 results.endAxis = clipModel->GetAxis(); 885 memset( &results.c, 0, sizeof( results.c ) ); 886 887 // always pushed when standing on the pusher 888 if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { 889 // move the entity colliding with all other entities except the pusher itself 890 ClipEntityTranslation( trace, check, NULL, clipModel, move ); 891 // if there is a collision 892 if ( trace.fraction < 1.0f ) { 893 // vector along which the entity is pushed 894 checkMove = move * trace.fraction; 895 // test if the entity can stay at it's partly pushed position by moving the entity in reverse only colliding with pusher 896 ClipEntityTranslation( results, check, clipModel, NULL, -(move - checkMove) ); 897 // if there is a collision 898 if ( results.fraction < 1.0f ) { 899 900 // FIXME: try to push the blocking entity as well or try to slide along collision plane(s)? 901 902 results.c.normal = -results.c.normal; 903 results.c.dist = -results.c.dist; 904 905 // the entity will be crushed between the pusher and some other entity 906 return PUSH_BLOCKED; 907 } 908 } 909 else { 910 // vector along which the entity is pushed 911 checkMove = move; 912 } 913 } 914 else { 915 // move entity in reverse only colliding with pusher 916 ClipEntityTranslation( results, check, clipModel, NULL, -move ); 917 // if no collision with the pusher then the entity is not pushed by the pusher 918 if ( results.fraction >= 1.0f ) { 919 return PUSH_NO; 920 } 921 // vector along which the entity is pushed 922 checkMove = move * (1.0f - results.fraction); 923 // move the entity colliding with all other entities except the pusher itself 924 ClipEntityTranslation( trace, check, NULL, clipModel, checkMove ); 925 // if there is a collisions 926 if ( trace.fraction < 1.0f ) { 927 928 results.c.normal = -results.c.normal; 929 results.c.dist = -results.c.dist; 930 931 // FIXME: try to push the blocking entity as well ? 932 // FIXME: handle sliding along more than one collision plane ? 933 // FIXME: this code has issues, player pushing box into corner in "maps/mre/aaron/test.map" 934 935 /* 936 oldOrigin = physics->GetOrigin(); 937 938 // movement still remaining 939 checkMove *= (1.0f - trace.fraction); 940 941 // project the movement along the collision plane 942 if ( !checkMove.ProjectAlongPlane( trace.c.normal, 0.1f, 1.001f ) ) { 943 return PUSH_BLOCKED; 944 } 945 checkMove *= 1.001f; 946 947 // move entity from collision point along the collision plane 948 physics->SetOrigin( trace.endpos ); 949 ClipEntityTranslation( trace, check, NULL, NULL, checkMove ); 950 951 if ( trace.fraction < 1.0f ) { 952 physics->SetOrigin( oldOrigin ); 953 return PUSH_BLOCKED; 954 } 955 956 checkMove = trace.endpos - oldOrigin; 957 958 // move entity in reverse only colliding with pusher 959 physics->SetOrigin( trace.endpos ); 960 ClipEntityTranslation( trace, check, clipModel, NULL, -move ); 961 962 physics->SetOrigin( oldOrigin ); 963 */ 964 if ( trace.fraction < 1.0f ) { 965 return PUSH_BLOCKED; 966 } 967 } 968 } 969 970 SaveEntityPosition( check ); 971 972 // translate the entity 973 physics->Translate( checkMove ); 974 975 #ifdef TRANSLATIONAL_PUSH_DEBUG 976 // set the pusher in the translated position 977 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() ); 978 if ( physics->ClipContents( clipModel ) ) { 979 if ( !startsolid ) { 980 int bah = 1; 981 } 982 } 983 #endif 984 985 return PUSH_OK; 986 } 987 988 /* 989 ============ 990 idPush::DiscardEntities 991 ============ 992 */ 993 int idPush::DiscardEntities( idEntity *entityList[], int numEntities, int flags, idEntity *pusher ) { 994 int i, num; 995 idEntity *check; 996 997 // remove all entities we cannot or should not push from the list 998 for ( num = i = 0; i < numEntities; i++ ) { 999 check = entityList[ i ]; 1000 1001 // if the physics object is not pushable 1002 if ( !check->GetPhysics()->IsPushable() ) { 1003 continue; 1004 } 1005 1006 // if the entity doesn't clip with this pusher 1007 if ( !( check->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) { 1008 continue; 1009 } 1010 1011 // don't push players in noclip mode 1012 if ( check->IsType( idPlayer::Type ) && static_cast<idPlayer *>(check)->noclip ) { 1013 continue; 1014 } 1015 1016 // if we should only push idMoveable entities 1017 if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !check->IsType( idMoveable::Type ) ) { 1018 continue; 1019 } 1020 1021 // if we shouldn't push entities the clip model rests upon 1022 if ( flags & PUSHFL_NOGROUNDENTITIES ) { 1023 if ( pusher->GetPhysics()->IsGroundEntity( check->entityNumber ) ) { 1024 continue; 1025 } 1026 } 1027 1028 // keep entity in list 1029 entityList[ num++ ] = entityList[i]; 1030 } 1031 1032 return num; 1033 } 1034 1035 /* 1036 ============ 1037 idPush::ClipTranslationalPush 1038 1039 Try to push other entities by moving the given entity. 1040 ============ 1041 */ 1042 float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags, 1043 const idVec3 &newOrigin, const idVec3 &translation ) { 1044 int i, listedEntities, res; 1045 idEntity *check, *entityList[ MAX_GENTITIES ]; 1046 idBounds bounds, pushBounds; 1047 idVec3 clipMove, clipOrigin, oldOrigin, dir, impulse; 1048 trace_t pushResults; 1049 bool wasEnabled; 1050 float totalMass; 1051 idClipModel *clipModel; 1052 1053 clipModel = pusher->GetPhysics()->GetClipModel(); 1054 1055 totalMass = 0.0f; 1056 1057 results.fraction = 1.0f; 1058 results.endpos = newOrigin; 1059 results.endAxis = clipModel->GetAxis(); 1060 memset( &results.c, 0, sizeof( results.c ) ); 1061 1062 if ( translation == vec3_origin ) { 1063 return totalMass; 1064 } 1065 1066 dir = translation; 1067 dir.Normalize(); 1068 dir.z += 1.0f; 1069 dir *= 10.0f; 1070 1071 // get bounds for the whole movement 1072 bounds = clipModel->GetBounds(); 1073 if ( bounds[0].x >= bounds[1].x ) { 1074 return totalMass; 1075 } 1076 pushBounds.FromBoundsTranslation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), translation ); 1077 1078 wasEnabled = clipModel->IsEnabled(); 1079 1080 // make sure we don't get the pushing clip model in the list 1081 clipModel->Disable(); 1082 1083 listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); 1084 1085 // discard entities we cannot or should not push 1086 listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher ); 1087 1088 if ( flags & PUSHFL_CLIP ) { 1089 1090 // can only clip movement of a trace model 1091 assert( clipModel->IsTraceModel() ); 1092 1093 // disable to be pushed entities for collision detection 1094 for ( i = 0; i < listedEntities; i++ ) { 1095 entityList[i]->GetPhysics()->DisableClip(); 1096 } 1097 1098 gameLocal.clip.Translation( results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL ); 1099 1100 // enable to be pushed entities for collision detection 1101 for ( i = 0; i < listedEntities; i++ ) { 1102 entityList[i]->GetPhysics()->EnableClip(); 1103 } 1104 1105 if ( results.fraction == 0.0f ) { 1106 if ( wasEnabled ) { 1107 clipModel->Enable(); 1108 } 1109 return totalMass; 1110 } 1111 1112 clipMove = results.endpos - clipModel->GetOrigin(); 1113 clipOrigin = results.endpos; 1114 1115 } 1116 else { 1117 1118 clipMove = translation; 1119 clipOrigin = newOrigin; 1120 } 1121 1122 // we have to enable the clip model because we use it during pushing 1123 clipModel->Enable(); 1124 1125 // save pusher old position 1126 oldOrigin = clipModel->GetOrigin(); 1127 1128 // try to push the entities 1129 for ( i = 0; i < listedEntities; i++ ) { 1130 1131 check = entityList[ i ]; 1132 1133 idPhysics *physics = check->GetPhysics(); 1134 1135 // disable the entity for collision detection 1136 physics->DisableClip(); 1137 1138 res = TryTranslatePushEntity( pushResults, check, clipModel, flags, clipOrigin, clipMove ); 1139 1140 // enable the entity for collision detection 1141 physics->EnableClip(); 1142 1143 // if the entity is pushed 1144 if ( res == PUSH_OK ) { 1145 // set the pusher in the translated position 1146 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() ); 1147 // the entity might be pushed off the ground 1148 physics->EvaluateContacts(); 1149 // put pusher back in old position 1150 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), oldOrigin, clipModel->GetAxis() ); 1151 1152 // wake up this object 1153 if ( flags & PUSHFL_APPLYIMPULSE ) { 1154 impulse = physics->GetMass() * dir; 1155 } else { 1156 impulse.Zero(); 1157 } 1158 check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), impulse ); 1159 1160 // add mass of pushed entity 1161 totalMass += physics->GetMass(); 1162 } 1163 1164 // if the entity is not blocking 1165 if ( res != PUSH_BLOCKED ) { 1166 continue; 1167 } 1168 1169 // if the blocking entity is a projectile 1170 if ( check->IsType( idProjectile::Type ) ) { 1171 check->ProcessEvent( &EV_Explode ); 1172 continue; 1173 } 1174 1175 // if blocking entities should be crushed 1176 if ( flags & PUSHFL_CRUSH ) { 1177 check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) ); 1178 continue; 1179 } 1180 1181 // if the entity is an active articulated figure and gibs 1182 if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) { 1183 if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) { 1184 check->ProcessEvent( &EV_Gib, "damage_Gib" ); 1185 } 1186 } 1187 1188 // if the entity is a moveable item and gibs 1189 if ( check->IsType( idMoveableItem::Type ) && check->spawnArgs.GetBool( "gib" ) ) { 1190 check->ProcessEvent( &EV_Gib, "damage_Gib" ); 1191 } 1192 1193 // blocked 1194 results = pushResults; 1195 results.fraction = 0.0f; 1196 results.endAxis = clipModel->GetAxis(); 1197 results.endpos = clipModel->GetOrigin(); 1198 results.c.entityNum = check->entityNumber; 1199 results.c.id = 0; 1200 1201 if ( !wasEnabled ) { 1202 clipModel->Disable(); 1203 } 1204 1205 return totalMass; 1206 } 1207 1208 if ( !wasEnabled ) { 1209 clipModel->Disable(); 1210 } 1211 1212 return totalMass; 1213 } 1214 1215 /* 1216 ============ 1217 idPush::ClipRotationalPush 1218 1219 Try to push other entities by moving the given entity. 1220 ============ 1221 */ 1222 float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags, 1223 const idMat3 &newAxis, const idRotation &rotation ) { 1224 int i, listedEntities, res; 1225 idEntity *check, *entityList[ MAX_GENTITIES ]; 1226 idBounds bounds, pushBounds; 1227 idRotation clipRotation; 1228 idMat3 clipAxis, oldAxis; 1229 trace_t pushResults; 1230 bool wasEnabled; 1231 float totalMass; 1232 idClipModel *clipModel; 1233 1234 clipModel = pusher->GetPhysics()->GetClipModel(); 1235 1236 totalMass = 0.0f; 1237 1238 results.fraction = 1.0f; 1239 results.endpos = clipModel->GetOrigin(); 1240 results.endAxis = newAxis; 1241 memset( &results.c, 0, sizeof( results.c ) ); 1242 1243 if ( !rotation.GetAngle() ) { 1244 return totalMass; 1245 } 1246 1247 // get bounds for the whole movement 1248 bounds = clipModel->GetBounds(); 1249 if ( bounds[0].x >= bounds[1].x ) { 1250 return totalMass; 1251 } 1252 pushBounds.FromBoundsRotation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), rotation ); 1253 1254 wasEnabled = clipModel->IsEnabled(); 1255 1256 // make sure we don't get the pushing clip model in the list 1257 clipModel->Disable(); 1258 1259 listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); 1260 1261 // discard entities we cannot or should not push 1262 listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher ); 1263 1264 if ( flags & PUSHFL_CLIP ) { 1265 1266 // can only clip movement of a trace model 1267 assert( clipModel->IsTraceModel() ); 1268 1269 // disable to be pushed entities for collision detection 1270 for ( i = 0; i < listedEntities; i++ ) { 1271 entityList[i]->GetPhysics()->DisableClip(); 1272 } 1273 1274 gameLocal.clip.Rotation( results, clipModel->GetOrigin(), rotation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL ); 1275 1276 // enable to be pushed entities for collision detection 1277 for ( i = 0; i < listedEntities; i++ ) { 1278 entityList[i]->GetPhysics()->EnableClip(); 1279 } 1280 1281 if ( results.fraction == 0.0f ) { 1282 if ( wasEnabled ) { 1283 clipModel->Enable(); 1284 } 1285 return totalMass; 1286 } 1287 1288 clipRotation = rotation * results.fraction; 1289 clipAxis = results.endAxis; 1290 } 1291 else { 1292 1293 clipRotation = rotation; 1294 clipAxis = newAxis; 1295 } 1296 1297 // we have to enable the clip model because we use it during pushing 1298 clipModel->Enable(); 1299 1300 // save pusher old position 1301 oldAxis = clipModel->GetAxis(); 1302 1303 // try to push all the entities 1304 for ( i = 0; i < listedEntities; i++ ) { 1305 1306 check = entityList[ i ]; 1307 1308 idPhysics *physics = check->GetPhysics(); 1309 1310 // disable the entity for collision detection 1311 physics->DisableClip(); 1312 1313 res = TryRotatePushEntity( pushResults, check, clipModel, flags, clipAxis, clipRotation ); 1314 1315 // enable the entity for collision detection 1316 physics->EnableClip(); 1317 1318 // if the entity is pushed 1319 if ( res == PUSH_OK ) { 1320 // set the pusher in the rotated position 1321 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); 1322 // the entity might be pushed off the ground 1323 physics->EvaluateContacts(); 1324 // put pusher back in old position 1325 clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), oldAxis ); 1326 1327 // wake up this object 1328 check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), vec3_origin ); 1329 1330 // add mass of pushed entity 1331 totalMass += physics->GetMass(); 1332 } 1333 1334 // if the entity is not blocking 1335 if ( res != PUSH_BLOCKED ) { 1336 continue; 1337 } 1338 1339 // if the blocking entity is a projectile 1340 if ( check->IsType( idProjectile::Type ) ) { 1341 check->ProcessEvent( &EV_Explode ); 1342 continue; 1343 } 1344 1345 // if blocking entities should be crushed 1346 if ( flags & PUSHFL_CRUSH ) { 1347 check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) ); 1348 continue; 1349 } 1350 1351 // if the entity is an active articulated figure and gibs 1352 if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) { 1353 if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) { 1354 check->ProcessEvent( &EV_Gib, "damage_Gib" ); 1355 } 1356 } 1357 1358 // blocked 1359 results = pushResults; 1360 results.fraction = 0.0f; 1361 results.endAxis = clipModel->GetAxis(); 1362 results.endpos = clipModel->GetOrigin(); 1363 results.c.entityNum = check->entityNumber; 1364 results.c.id = 0; 1365 1366 if ( !wasEnabled ) { 1367 clipModel->Disable(); 1368 } 1369 1370 return totalMass; 1371 } 1372 1373 if ( !wasEnabled ) { 1374 clipModel->Disable(); 1375 } 1376 1377 return totalMass; 1378 } 1379 1380 #endif /* !NEW_PUSH */ 1381 1382 1383 /* 1384 ============ 1385 idPush::ClipPush 1386 1387 Try to push other entities by moving the given entity. 1388 ============ 1389 */ 1390 float idPush::ClipPush( trace_t &results, idEntity *pusher, const int flags, 1391 const idVec3 &oldOrigin, const idMat3 &oldAxis, 1392 idVec3 &newOrigin, idMat3 &newAxis ) { 1393 idVec3 translation; 1394 idRotation rotation; 1395 float mass; 1396 1397 mass = 0.0f; 1398 1399 results.fraction = 1.0f; 1400 results.endpos = newOrigin; 1401 results.endAxis = newAxis; 1402 memset( &results.c, 0, sizeof( results.c ) ); 1403 1404 // translational push 1405 translation = newOrigin - oldOrigin; 1406 1407 // if the pusher translates 1408 if ( translation != vec3_origin ) { 1409 1410 mass += ClipTranslationalPush( results, pusher, flags, newOrigin, translation ); 1411 if ( results.fraction < 1.0f ) { 1412 newOrigin = oldOrigin; 1413 newAxis = oldAxis; 1414 return mass; 1415 } 1416 } else { 1417 newOrigin = oldOrigin; 1418 } 1419 1420 // rotational push 1421 rotation = ( oldAxis.Transpose() * newAxis ).ToRotation(); 1422 rotation.SetOrigin( newOrigin ); 1423 rotation.Normalize180(); 1424 rotation.ReCalculateMatrix(); // recalculate the rotation matrix to avoid accumulating rounding errors 1425 1426 // if the pusher rotates 1427 if ( rotation.GetAngle() != 0.0f ) { 1428 1429 // recalculate new axis to avoid floating point rounding problems 1430 newAxis = oldAxis * rotation.ToMat3(); 1431 newAxis.OrthoNormalizeSelf(); 1432 newAxis.FixDenormals(); 1433 newAxis.FixDegeneracies(); 1434 1435 pusher->GetPhysics()->GetClipModel()->SetPosition( newOrigin, oldAxis ); 1436 1437 mass += ClipRotationalPush( results, pusher, flags, newAxis, rotation ); 1438 if ( results.fraction < 1.0f ) { 1439 newOrigin = oldOrigin; 1440 newAxis = oldAxis; 1441 return mass; 1442 } 1443 } else { 1444 newAxis = oldAxis; 1445 } 1446 1447 return mass; 1448 }