doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Push.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 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 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 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "../Game_local.h"
33 
34 
35 /*
36 ============
37 idPush::InitSavingPushedEntityPositions
38 ============
39 */
41  numPushed = 0;
42 }
43 
44 /*
45 ============
46 idPush::SaveEntityPosition
47 ============
48 */
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 */
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
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++ ) {
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++ ) {
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++ ) {
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++ ) {
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 }
virtual const idVec3 & GetOrigin(int id=0) const =0
idEntity * GetEntity(void) const
Definition: Clip.h:178
void ClipEntityTranslation(trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idVec3 &translation)
Definition: Push.cpp:686
float ClipPush(trace_t &results, idEntity *pusher, const int flags, const idVec3 &oldOrigin, const idMat3 &oldAxis, idVec3 &newOrigin, idMat3 &newAxis)
Definition: Push.cpp:1390
virtual void EnableClip(void)=0
float Normalize(void)
Definition: Vector.h:646
assert(prefInfo.fullscreenBtn)
void Link(idClip &clp)
Definition: Clip.cpp:545
#define CLIPMODEL_ID_TO_JOINT_HANDLE(id)
Definition: Clip.h:40
idMat3 mat3_identity(idVec3(1, 0, 0), idVec3(0, 1, 0), idVec3(0, 0, 1))
virtual int GetClipMask(int id=-1) const =0
bool groundContact
Definition: Push.h:82
idClip clip
Definition: Game_local.h:296
#define PUSHFL_CLIP
Definition: Push.h:42
#define MAX_GENTITIES
Definition: Game_local.h:83
bool IsEnabled(void) const
Definition: Clip.h:226
virtual void SaveState(void)=0
void ClipEntityRotation(trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idRotation &rotation)
Definition: Push.cpp:668
virtual void Rotate(const idRotation &rotation, int id=-1)=0
void Translate(const idVec3 &translation)
Definition: Clip.h:139
void SaveEntityPosition(idEntity *ent)
Definition: Push.cpp:49
idEntity * ent
Definition: Push.h:80
idMat3 Transpose(void) const
Definition: Matrix.h:677
void InitSavingPushedEntityPositions(void)
Definition: Push.cpp:40
void void void void void Error(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:783
bool IsType(const idTypeInfo &c) const
Definition: Class.h:337
const idMat3 & GetAxis(void) const
Definition: Clip.h:210
GLenum GLsizei n
Definition: glext.h:3705
float z
Definition: Vector.h:320
const idEventDef EV_Gib("gib","s")
bool ProcessEvent(const idEventDef *ev)
Definition: Class.cpp:858
virtual int GetNumClipModels(void) const =0
Definition: Vector.h:316
float ClipTranslationalPush(trace_t &results, idEntity *pusher, const int flags, const idVec3 &newOrigin, const idVec3 &move)
Definition: Push.cpp:1042
virtual void Translate(const idVec3 &translation, int id=-1)=0
#define PUSHFL_NOGROUNDENTITIES
Definition: Push.h:41
const idVec3 & GetOrigin(void) const
Definition: Clip.h:206
void Scale(const float s)
Definition: Rotation.h:136
virtual void ApplyImpulse(idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse)
Definition: Entity.cpp:2887
bool IsRotated(void) const
Definition: Matrix.h:624
idDict spawnArgs
Definition: Entity.h:122
GLenum GLint x
Definition: glext.h:2849
int i
Definition: process.py:33
virtual bool IsGroundClipModel(int entityNum, int id) const =0
contactInfo_t c
void Normalize180(void)
Definition: Rotation.cpp:134
GLuint GLuint num
Definition: glext.h:5390
void FromBoundsRotation(const idBounds &bounds, const idVec3 &origin, const idMat3 &axis, const idRotation &rotation)
Definition: Bounds.cpp:386
int test(char *url)
Definition: lib500.c:3
void RestorePushedEntityPositions(void)
Definition: Push.cpp:84
void SetDeltaViewAngles(const idAngles &delta)
Definition: Actor.cpp:1821
virtual void RestoreState(void)=0
const idEventDef EV_Explode("<explode>", NULL)
int EntitiesTouchingBounds(const idBounds &bounds, int contentMask, idEntity **entityList, int maxCount) const
Definition: Clip.cpp:833
float fraction
idVec3 endpos
list l
Definition: prepare.py:17
virtual const idBounds & GetBounds(int id=-1) const =0
#define PUSHFL_ONLYMOVEABLE
Definition: Push.h:40
void FromBoundsTranslation(const idBounds &bounds, const idVec3 &origin, const idMat3 &axis, const idVec3 &translation)
Definition: Bounds.cpp:290
float GetAngle(void) const
Definition: Rotation.h:154
virtual void ClipTranslation(trace_t &results, const idVec3 &translation, const idClipModel *model) const =0
bool FixDenormals(void)
Definition: Matrix.h:645
idPhysics * GetPhysics(void) const
Definition: Entity.cpp:2607
int numPushed
Definition: Push.h:77
int TryTranslatePushEntity(trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, const idVec3 &newOrigin, const idVec3 &move)
Definition: Push.cpp:866
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
const idAngles & GetDeltaViewAngles(void) const
Definition: Actor.cpp:1812
bool RotateEntityToAxial(idEntity *ent, idVec3 rotationPoint)
Definition: Push.cpp:105
virtual float GetMass(int id=-1) const =0
bool GetBool(const char *key, const char *defaultString="0") const
Definition: Dict.h:256
#define NULL
Definition: Lib.h:88
const idBounds & GetBounds(void) const
Definition: Clip.h:198
virtual void SetOrigin(const idVec3 &newOrigin, int id=-1)=0
bool IsTraceModel(void) const
Definition: Clip.h:218
idMat3 endAxis
int pushedGroupSize
Definition: Push.h:85
int GetId(void) const
Definition: Clip.h:186
virtual const idMat3 & GetAxis(int id=0) const =0
idGameLocal gameLocal
Definition: Game_local.cpp:64
virtual void Damage(idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location)
Definition: Entity.cpp:3061
#define PUSHFL_APPLYIMPULSE
Definition: Push.h:44
idAngles deltaViewAngles
Definition: Push.h:75
idMat3 & OrthoNormalizeSelf(void)
Definition: Matrix.h:668
void Set(const idVec3 &rotationOrigin, const idVec3 &rotationVec, const float rotationAngle)
Definition: Rotation.h:108
virtual void DisableClip(void)=0
virtual idClipModel * GetClipModel(int id=0) const =0
int DiscardEntities(idEntity *entityList[], int numEntities, int flags, idEntity *pusher)
Definition: Push.cpp:993
const idVec3 & GetVec(void) const
Definition: Rotation.h:150
int TryRotatePushEntity(trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, const idMat3 &newAxis, const idRotation &rotation)
Definition: Push.cpp:708
Definition: Matrix.h:333
float yaw
Definition: Angles.h:54
void SetOrigin(const idVec3 &rotationOrigin)
Definition: Rotation.h:115
virtual bool IsPushable(void) const =0
void Enable(void)
Definition: Clip.h:150
virtual void ClipRotation(trace_t &results, const idRotation &rotation, const idClipModel *model) const =0
tuple f
Definition: idal.py:89
Definition: Actor.h:111
const idMat3 & ToMat3(void) const
Definition: Rotation.cpp:60
virtual bool IsGroundEntity(int entityNum) const =0
bool Rotation(trace_t &results, const idVec3 &start, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity)
Definition: Clip.cpp:1130
virtual int ClipContents(const idClipModel *model) const =0
void ReCalculateMatrix(void)
Definition: Rotation.h:141
idEntity * ent
Definition: Push.h:74
idRotation ToRotation(void) const
Definition: Matrix.cpp:246
virtual bool EvaluateContacts(void)=0
#define PUSHFL_CRUSH
Definition: Push.h:43
const idVec3 & GetOrigin(void) const
Definition: Rotation.h:146
virtual int GetContents(int id=-1) const =0
GLuint res
Definition: glext.h:5385
void Disable(void)
Definition: Clip.h:154
GLint j
Definition: qgl.h:264
idBounds & ExpandSelf(const float d)
Definition: Bounds.h:322
void Rotate(const idRotation &rotation)
Definition: Clip.h:144
struct idPush::pushedGroup_s pushedGroup[MAX_GENTITIES]
bool Translation(trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity)
Definition: Clip.cpp:1056
int entityNumber
Definition: Entity.h:111
void Zero(void)
Definition: Vector.h:415
bool FixDegeneracies(void)
Definition: Matrix.h:638
void SetPosition(const idVec3 &newOrigin, const idMat3 &newAxis)
Definition: Clip.cpp:444
void RotatePoint(idVec3 &point) const
Definition: Rotation.h:204
struct idPush::pushed_s pushed[MAX_GENTITIES]
virtual void SetAxis(const idMat3 &newAxis, int id=-1)=0
GLdouble GLdouble t
Definition: glext.h:2943
float ClipRotationalPush(trace_t &results, idEntity *pusher, const int flags, const idMat3 &newAxis, const idRotation &rotation)
Definition: Push.cpp:1222