Push.cpp 39 KB

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