Clip.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  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. #pragma hdrstop
  21. #include "../../idlib/precompiled.h"
  22. #include "../Game_local.h"
  23. #define MAX_SECTOR_DEPTH 12
  24. #define MAX_SECTORS ((1<<(MAX_SECTOR_DEPTH+1))-1)
  25. typedef struct clipSector_s {
  26. int axis; // -1 = leaf node
  27. float dist;
  28. struct clipSector_s * children[2];
  29. struct clipLink_s * clipLinks;
  30. } clipSector_t;
  31. typedef struct clipLink_s {
  32. idClipModel * clipModel;
  33. struct clipSector_s * sector;
  34. struct clipLink_s * prevInSector;
  35. struct clipLink_s * nextInSector;
  36. struct clipLink_s * nextLink;
  37. } clipLink_t;
  38. typedef struct trmCache_s {
  39. idTraceModel trm;
  40. int refCount;
  41. float volume;
  42. idVec3 centerOfMass;
  43. idMat3 inertiaTensor;
  44. } trmCache_t;
  45. idVec3 vec3_boxEpsilon( CM_BOX_EPSILON, CM_BOX_EPSILON, CM_BOX_EPSILON );
  46. idBlockAlloc<clipLink_t, 1024> clipLinkAllocator;
  47. /*
  48. ===============================================================
  49. idClipModel trace model cache
  50. ===============================================================
  51. */
  52. static idList<trmCache_s*> traceModelCache;
  53. static idList<trmCache_s*> traceModelCache_Unsaved;
  54. static idHashIndex traceModelHash;
  55. static idHashIndex traceModelHash_Unsaved;
  56. const static int TRACE_MODEL_SAVED = BIT( 16 );
  57. /*
  58. ===============
  59. idClipModel::ClearTraceModelCache
  60. ===============
  61. */
  62. void idClipModel::ClearTraceModelCache() {
  63. traceModelCache.DeleteContents( true );
  64. traceModelCache_Unsaved.DeleteContents( true );
  65. traceModelHash.Free();
  66. traceModelHash_Unsaved.Free();
  67. }
  68. /*
  69. ===============
  70. idClipModel::TraceModelCacheSize
  71. ===============
  72. */
  73. int idClipModel::TraceModelCacheSize() {
  74. return traceModelCache.Num() * sizeof( idTraceModel );
  75. }
  76. /*
  77. ===============
  78. idClipModel::AllocTraceModel
  79. ===============
  80. */
  81. int idClipModel::AllocTraceModel( const idTraceModel &trm, bool persistantThroughSaves ) {
  82. int i, hashKey, traceModelIndex;
  83. trmCache_t *entry;
  84. hashKey = GetTraceModelHashKey( trm );
  85. if( persistantThroughSaves ) {
  86. // Look Inside the saved list.
  87. for ( i = traceModelHash.First( hashKey ); i >= 0; i = traceModelHash.Next( i ) ) {
  88. if ( traceModelCache[i]->trm == trm ) {
  89. traceModelCache[i]->refCount++;
  90. int flagged_index = i | TRACE_MODEL_SAVED;
  91. return flagged_index;
  92. }
  93. }
  94. } else {
  95. // Look inside the unsaved list.
  96. for ( i = traceModelHash_Unsaved.First( hashKey ); i >= 0; i = traceModelHash_Unsaved.Next( i ) ) {
  97. if ( traceModelCache_Unsaved[i]->trm == trm ) {
  98. traceModelCache_Unsaved[i]->refCount++;
  99. return i;
  100. }
  101. }
  102. }
  103. entry = new (TAG_PHYSICS_CLIP) trmCache_t;
  104. entry->trm = trm;
  105. entry->trm.GetMassProperties( 1.0f, entry->volume, entry->centerOfMass, entry->inertiaTensor );
  106. entry->refCount = 1;
  107. if( persistantThroughSaves ) {
  108. traceModelIndex = traceModelCache.Append( entry );
  109. traceModelHash.Add( hashKey, traceModelIndex );
  110. // Set the saved bit.
  111. traceModelIndex |= TRACE_MODEL_SAVED;
  112. } else {
  113. traceModelIndex = traceModelCache_Unsaved.Append( entry );
  114. traceModelHash_Unsaved.Add( hashKey, traceModelIndex );
  115. // remove the saved bit
  116. traceModelIndex &= ~TRACE_MODEL_SAVED;
  117. }
  118. return traceModelIndex;
  119. }
  120. /*
  121. ===============
  122. idClipModel::FreeTraceModel
  123. ===============
  124. */
  125. void idClipModel::FreeTraceModel( int traceModelIndex ) {
  126. int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED;
  127. // Check which cache we are using.
  128. if( traceModelIndex & TRACE_MODEL_SAVED ) {
  129. if ( realTraceModelIndex < 0 || realTraceModelIndex >= traceModelCache.Num() || traceModelCache[realTraceModelIndex]->refCount <= 0 ) {
  130. gameLocal.Warning( "idClipModel::FreeTraceModel: tried to free uncached trace model" );
  131. return;
  132. }
  133. traceModelCache[realTraceModelIndex]->refCount--;
  134. } else {
  135. if ( realTraceModelIndex < 0 || realTraceModelIndex >= traceModelCache_Unsaved.Num() || traceModelCache_Unsaved[realTraceModelIndex]->refCount <= 0 ) {
  136. gameLocal.Warning( "idClipModel::FreeTraceModel: tried to free uncached trace model" );
  137. return;
  138. }
  139. traceModelCache_Unsaved[realTraceModelIndex]->refCount--;
  140. }
  141. }
  142. /*
  143. ===============
  144. idClipModel::GetCachedTraceModel
  145. ===============
  146. */
  147. idTraceModel *idClipModel::GetCachedTraceModel( int traceModelIndex ) {
  148. int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED;
  149. if( traceModelIndex & TRACE_MODEL_SAVED ) {
  150. return &traceModelCache[realTraceModelIndex]->trm;
  151. } else {
  152. return &traceModelCache_Unsaved[realTraceModelIndex]->trm;
  153. }
  154. }
  155. /*
  156. ===============
  157. idClipModel::GetCachedTraceModel
  158. ===============
  159. */
  160. trmCache_t * idClipModel::GetTraceModelEntry( int traceModelIndex ) {
  161. int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED;
  162. if( traceModelIndex & TRACE_MODEL_SAVED ) {
  163. return traceModelCache[realTraceModelIndex];
  164. } else {
  165. return traceModelCache_Unsaved[realTraceModelIndex];
  166. }
  167. }
  168. /*
  169. ===============
  170. idClipModel::GetTraceModelHashKey
  171. ===============
  172. */
  173. int idClipModel::GetTraceModelHashKey( const idTraceModel &trm ) {
  174. const idVec3 &v = trm.bounds[0];
  175. return ( trm.type << 8 ) ^ ( trm.numVerts << 4 ) ^ ( trm.numEdges << 2 ) ^ ( trm.numPolys << 0 ) ^ idMath::FloatHash( v.ToFloatPtr(), v.GetDimension() );
  176. }
  177. /*
  178. ===============
  179. idClipModel::SaveTraceModels
  180. ===============
  181. */
  182. void idClipModel::SaveTraceModels( idSaveGame *savefile ) {
  183. int i;
  184. savefile->WriteInt( traceModelCache.Num() );
  185. for ( i = 0; i < traceModelCache.Num(); i++ ) {
  186. trmCache_t *entry = traceModelCache[i];
  187. savefile->WriteTraceModel( entry->trm );
  188. savefile->WriteFloat( entry->volume );
  189. savefile->WriteVec3( entry->centerOfMass );
  190. savefile->WriteMat3( entry->inertiaTensor );
  191. }
  192. }
  193. /*
  194. ===============
  195. idClipModel::RestoreTraceModels
  196. ===============
  197. */
  198. void idClipModel::RestoreTraceModels( idRestoreGame *savefile ) {
  199. int i, num;
  200. ClearTraceModelCache();
  201. savefile->ReadInt( num );
  202. traceModelCache.SetNum( num );
  203. for ( i = 0; i < num; i++ ) {
  204. trmCache_t *entry = new (TAG_PHYSICS_CLIP) trmCache_t;
  205. savefile->ReadTraceModel( entry->trm );
  206. savefile->ReadFloat( entry->volume );
  207. savefile->ReadVec3( entry->centerOfMass );
  208. savefile->ReadMat3( entry->inertiaTensor );
  209. entry->refCount = 0;
  210. traceModelCache[i] = entry;
  211. traceModelHash.Add( GetTraceModelHashKey( entry->trm ), i );
  212. }
  213. }
  214. /*
  215. ===============================================================
  216. idClipModel
  217. ===============================================================
  218. */
  219. /*
  220. ================
  221. idClipModel::LoadModel
  222. ================
  223. */
  224. bool idClipModel::LoadModel( const char *name ) {
  225. renderModelHandle = -1;
  226. if ( traceModelIndex != -1 ) {
  227. FreeTraceModel( traceModelIndex );
  228. traceModelIndex = -1;
  229. }
  230. collisionModelHandle = collisionModelManager->LoadModel( name );
  231. if ( collisionModelHandle ) {
  232. collisionModelManager->GetModelBounds( collisionModelHandle, bounds );
  233. collisionModelManager->GetModelContents( collisionModelHandle, contents );
  234. return true;
  235. } else {
  236. bounds.Zero();
  237. return false;
  238. }
  239. }
  240. /*
  241. ================
  242. idClipModel::LoadModel
  243. ================
  244. */
  245. void idClipModel::LoadModel( const idTraceModel &trm, bool persistantThroughSave ) {
  246. collisionModelHandle = 0;
  247. renderModelHandle = -1;
  248. if ( traceModelIndex != -1 ) {
  249. FreeTraceModel( traceModelIndex );
  250. }
  251. traceModelIndex = AllocTraceModel( trm, persistantThroughSave );
  252. bounds = trm.bounds;
  253. }
  254. /*
  255. ================
  256. idClipModel::LoadModel
  257. ================
  258. */
  259. void idClipModel::LoadModel( const int renderModelHandle ) {
  260. collisionModelHandle = 0;
  261. this->renderModelHandle = renderModelHandle;
  262. if ( renderModelHandle != -1 ) {
  263. const renderEntity_t *renderEntity = gameRenderWorld->GetRenderEntity( renderModelHandle );
  264. if ( renderEntity ) {
  265. bounds = renderEntity->bounds;
  266. }
  267. }
  268. if ( traceModelIndex != -1 ) {
  269. FreeTraceModel( traceModelIndex );
  270. traceModelIndex = -1;
  271. }
  272. }
  273. /*
  274. ================
  275. idClipModel::Init
  276. ================
  277. */
  278. void idClipModel::Init() {
  279. enabled = true;
  280. entity = NULL;
  281. id = 0;
  282. owner = NULL;
  283. origin.Zero();
  284. axis.Identity();
  285. bounds.Zero();
  286. absBounds.Zero();
  287. material = NULL;
  288. contents = CONTENTS_BODY;
  289. collisionModelHandle = 0;
  290. renderModelHandle = -1;
  291. traceModelIndex = -1;
  292. clipLinks = NULL;
  293. touchCount = -1;
  294. }
  295. /*
  296. ================
  297. idClipModel::idClipModel
  298. ================
  299. */
  300. idClipModel::idClipModel() {
  301. Init();
  302. }
  303. /*
  304. ================
  305. idClipModel::idClipModel
  306. ================
  307. */
  308. idClipModel::idClipModel( const char *name ) {
  309. Init();
  310. LoadModel( name );
  311. }
  312. /*
  313. ================
  314. idClipModel::idClipModel
  315. ================
  316. */
  317. idClipModel::idClipModel( const idTraceModel &trm ) {
  318. Init();
  319. LoadModel( trm, true );
  320. }
  321. /*
  322. ================
  323. idClipModel::idClipModel
  324. ================
  325. */
  326. idClipModel::idClipModel( const idTraceModel &trm, bool persistantThroughSave ) {
  327. Init();
  328. LoadModel( trm, persistantThroughSave );
  329. }
  330. /*
  331. ================
  332. idClipModel::idClipModel
  333. ================
  334. */
  335. idClipModel::idClipModel( const int renderModelHandle ) {
  336. Init();
  337. contents = CONTENTS_RENDERMODEL;
  338. LoadModel( renderModelHandle );
  339. }
  340. /*
  341. ================
  342. idClipModel::idClipModel
  343. ================
  344. */
  345. idClipModel::idClipModel( const idClipModel *model ) {
  346. enabled = model->enabled;
  347. entity = model->entity;
  348. id = model->id;
  349. owner = model->owner;
  350. origin = model->origin;
  351. axis = model->axis;
  352. bounds = model->bounds;
  353. absBounds = model->absBounds;
  354. material = model->material;
  355. contents = model->contents;
  356. collisionModelHandle = model->collisionModelHandle;
  357. traceModelIndex = -1;
  358. if ( model->traceModelIndex != -1 ) {
  359. LoadModel( *GetCachedTraceModel( model->traceModelIndex ) );
  360. }
  361. renderModelHandle = model->renderModelHandle;
  362. clipLinks = NULL;
  363. touchCount = -1;
  364. }
  365. /*
  366. ================
  367. idClipModel::~idClipModel
  368. ================
  369. */
  370. idClipModel::~idClipModel() {
  371. // make sure the clip model is no longer linked
  372. Unlink();
  373. if ( traceModelIndex != -1 ) {
  374. FreeTraceModel( traceModelIndex );
  375. }
  376. }
  377. /*
  378. ================
  379. idClipModel::Save
  380. ================
  381. */
  382. void idClipModel::Save( idSaveGame *savefile ) const {
  383. savefile->WriteBool( enabled );
  384. savefile->WriteObject( entity );
  385. savefile->WriteInt( id );
  386. savefile->WriteObject( owner );
  387. savefile->WriteVec3( origin );
  388. savefile->WriteMat3( axis );
  389. savefile->WriteBounds( bounds );
  390. savefile->WriteBounds( absBounds );
  391. savefile->WriteMaterial( material );
  392. savefile->WriteInt( contents );
  393. if ( collisionModelHandle >= 0 ) {
  394. savefile->WriteString( collisionModelManager->GetModelName( collisionModelHandle ) );
  395. } else {
  396. savefile->WriteString( "" );
  397. }
  398. savefile->WriteInt( traceModelIndex );
  399. savefile->WriteInt( renderModelHandle );
  400. savefile->WriteBool( clipLinks != NULL );
  401. savefile->WriteInt( touchCount );
  402. }
  403. /*
  404. ================
  405. idClipModel::Restore
  406. ================
  407. */
  408. void idClipModel::Restore( idRestoreGame *savefile ) {
  409. idStr collisionModelName;
  410. bool linked;
  411. savefile->ReadBool( enabled );
  412. savefile->ReadObject( reinterpret_cast<idClass *&>( entity ) );
  413. savefile->ReadInt( id );
  414. savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
  415. savefile->ReadVec3( origin );
  416. savefile->ReadMat3( axis );
  417. savefile->ReadBounds( bounds );
  418. savefile->ReadBounds( absBounds );
  419. savefile->ReadMaterial( material );
  420. savefile->ReadInt( contents );
  421. savefile->ReadString( collisionModelName );
  422. if ( collisionModelName.Length() ) {
  423. collisionModelHandle = collisionModelManager->LoadModel( collisionModelName );
  424. } else {
  425. collisionModelHandle = -1;
  426. }
  427. savefile->ReadInt( traceModelIndex );
  428. if ( traceModelIndex >= 0 ) {
  429. int realIndex = traceModelIndex & ~TRACE_MODEL_SAVED;
  430. traceModelCache[realIndex]->refCount++;
  431. }
  432. savefile->ReadInt( renderModelHandle );
  433. savefile->ReadBool( linked );
  434. savefile->ReadInt( touchCount );
  435. // the render model will be set when the clip model is linked
  436. renderModelHandle = -1;
  437. clipLinks = NULL;
  438. touchCount = -1;
  439. if ( linked ) {
  440. Link( gameLocal.clip, entity, id, origin, axis, renderModelHandle );
  441. }
  442. }
  443. /*
  444. ================
  445. idClipModel::SetPosition
  446. ================
  447. */
  448. void idClipModel::SetPosition( const idVec3 &newOrigin, const idMat3 &newAxis ) {
  449. if ( clipLinks ) {
  450. Unlink(); // unlink from old position
  451. }
  452. origin = newOrigin;
  453. axis = newAxis;
  454. }
  455. /*
  456. ================
  457. idClipModel::Handle
  458. ================
  459. */
  460. cmHandle_t idClipModel::Handle() const {
  461. assert( renderModelHandle == -1 );
  462. if ( collisionModelHandle ) {
  463. return collisionModelHandle;
  464. } else if ( traceModelIndex != -1 ) {
  465. return collisionModelManager->SetupTrmModel( *GetCachedTraceModel( traceModelIndex ), material );
  466. } else {
  467. // this happens in multiplayer on the combat models
  468. gameLocal.Warning( "idClipModel::Handle: clip model %d on '%s' (%x) is not a collision or trace model", id, entity->name.c_str(), entity->entityNumber );
  469. return 0;
  470. }
  471. }
  472. /*
  473. ================
  474. idClipModel::GetMassProperties
  475. ================
  476. */
  477. void idClipModel::GetMassProperties( const float density, float &mass, idVec3 &centerOfMass, idMat3 &inertiaTensor ) const {
  478. if ( traceModelIndex == -1 ) {
  479. gameLocal.Error( "idClipModel::GetMassProperties: clip model %d on '%s' is not a trace model\n", id, entity->name.c_str() );
  480. }
  481. trmCache_t *entry = GetTraceModelEntry( traceModelIndex ); //traceModelCache[traceModelIndex];
  482. mass = entry->volume * density;
  483. centerOfMass = entry->centerOfMass;
  484. inertiaTensor = density * entry->inertiaTensor;
  485. }
  486. /*
  487. ===============
  488. idClipModel::Unlink
  489. ===============
  490. */
  491. void idClipModel::Unlink() {
  492. clipLink_t *link;
  493. for ( link = clipLinks; link; link = clipLinks ) {
  494. clipLinks = link->nextLink;
  495. if ( link->prevInSector ) {
  496. link->prevInSector->nextInSector = link->nextInSector;
  497. } else {
  498. link->sector->clipLinks = link->nextInSector;
  499. }
  500. if ( link->nextInSector ) {
  501. link->nextInSector->prevInSector = link->prevInSector;
  502. }
  503. clipLinkAllocator.Free( link );
  504. }
  505. }
  506. /*
  507. ===============
  508. idClipModel::Link_r
  509. ===============
  510. */
  511. void idClipModel::Link_r( struct clipSector_s *node ) {
  512. clipLink_t *link;
  513. while( node->axis != -1 ) {
  514. if ( absBounds[0][node->axis] > node->dist ) {
  515. node = node->children[0];
  516. } else if ( absBounds[1][node->axis] < node->dist ) {
  517. node = node->children[1];
  518. } else {
  519. Link_r( node->children[0] );
  520. node = node->children[1];
  521. }
  522. }
  523. link = clipLinkAllocator.Alloc();
  524. link->clipModel = this;
  525. link->sector = node;
  526. link->nextInSector = node->clipLinks;
  527. link->prevInSector = NULL;
  528. if ( node->clipLinks ) {
  529. node->clipLinks->prevInSector = link;
  530. }
  531. node->clipLinks = link;
  532. link->nextLink = clipLinks;
  533. clipLinks = link;
  534. }
  535. /*
  536. ===============
  537. idClipModel::Link
  538. ===============
  539. */
  540. void idClipModel::Link( idClip &clp ) {
  541. assert( idClipModel::entity );
  542. if ( !idClipModel::entity ) {
  543. return;
  544. }
  545. if ( clipLinks ) {
  546. Unlink(); // unlink from old position
  547. }
  548. if ( bounds.IsCleared() ) {
  549. return;
  550. }
  551. // set the abs box
  552. if ( axis.IsRotated() ) {
  553. // expand for rotation
  554. absBounds.FromTransformedBounds( bounds, origin, axis );
  555. } else {
  556. // normal
  557. absBounds[0] = bounds[0] + origin;
  558. absBounds[1] = bounds[1] + origin;
  559. }
  560. // because movement is clipped an epsilon away from an actual edge,
  561. // we must fully check even when bounding boxes don't quite touch
  562. absBounds[0] -= vec3_boxEpsilon;
  563. absBounds[1] += vec3_boxEpsilon;
  564. Link_r( clp.clipSectors );
  565. }
  566. /*
  567. ===============
  568. idClipModel::Link
  569. ===============
  570. */
  571. void idClipModel::Link( idClip &clp, idEntity *ent, int newId, const idVec3 &newOrigin, const idMat3 &newAxis, int renderModelHandle ) {
  572. this->entity = ent;
  573. this->id = newId;
  574. this->origin = newOrigin;
  575. this->axis = newAxis;
  576. if ( renderModelHandle != -1 ) {
  577. this->renderModelHandle = renderModelHandle;
  578. const renderEntity_t *renderEntity = gameRenderWorld->GetRenderEntity( renderModelHandle );
  579. if ( renderEntity ) {
  580. this->bounds = renderEntity->bounds;
  581. }
  582. }
  583. this->Link( clp );
  584. }
  585. /*
  586. ============
  587. idClipModel::CheckModel
  588. ============
  589. */
  590. cmHandle_t idClipModel::CheckModel( const char *name ) {
  591. return collisionModelManager->LoadModel( name );
  592. }
  593. /*
  594. ===============================================================
  595. idClip
  596. ===============================================================
  597. */
  598. /*
  599. ===============
  600. idClip::idClip
  601. ===============
  602. */
  603. idClip::idClip() {
  604. numClipSectors = 0;
  605. clipSectors = NULL;
  606. worldBounds.Zero();
  607. numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0;
  608. }
  609. /*
  610. ===============
  611. idClip::CreateClipSectors_r
  612. Builds a uniformly subdivided tree for the given world size
  613. ===============
  614. */
  615. clipSector_t *idClip::CreateClipSectors_r( const int depth, const idBounds &bounds, idVec3 &maxSector ) {
  616. int i;
  617. clipSector_t *anode;
  618. idVec3 size;
  619. idBounds front, back;
  620. anode = &clipSectors[idClip::numClipSectors];
  621. idClip::numClipSectors++;
  622. if ( depth == MAX_SECTOR_DEPTH ) {
  623. anode->axis = -1;
  624. anode->children[0] = anode->children[1] = NULL;
  625. for ( i = 0; i < 3; i++ ) {
  626. if ( bounds[1][i] - bounds[0][i] > maxSector[i] ) {
  627. maxSector[i] = bounds[1][i] - bounds[0][i];
  628. }
  629. }
  630. return anode;
  631. }
  632. size = bounds[1] - bounds[0];
  633. if ( size[0] >= size[1] && size[0] >= size[2] ) {
  634. anode->axis = 0;
  635. } else if ( size[1] >= size[0] && size[1] >= size[2] ) {
  636. anode->axis = 1;
  637. } else {
  638. anode->axis = 2;
  639. }
  640. anode->dist = 0.5f * ( bounds[1][anode->axis] + bounds[0][anode->axis] );
  641. front = bounds;
  642. back = bounds;
  643. front[0][anode->axis] = back[1][anode->axis] = anode->dist;
  644. anode->children[0] = CreateClipSectors_r( depth+1, front, maxSector );
  645. anode->children[1] = CreateClipSectors_r( depth+1, back, maxSector );
  646. return anode;
  647. }
  648. /*
  649. ===============
  650. idClip::Init
  651. ===============
  652. */
  653. void idClip::Init() {
  654. cmHandle_t h;
  655. idVec3 size, maxSector = vec3_origin;
  656. // clear clip sectors
  657. clipSectors = new (TAG_PHYSICS_CLIP) clipSector_t[MAX_SECTORS];
  658. memset( clipSectors, 0, MAX_SECTORS * sizeof( clipSector_t ) );
  659. numClipSectors = 0;
  660. touchCount = -1;
  661. // get world map bounds
  662. h = collisionModelManager->LoadModel( "worldMap" );
  663. collisionModelManager->GetModelBounds( h, worldBounds );
  664. // create world sectors
  665. CreateClipSectors_r( 0, worldBounds, maxSector );
  666. size = worldBounds[1] - worldBounds[0];
  667. gameLocal.Printf( "map bounds are (%1.1f, %1.1f, %1.1f)\n", size[0], size[1], size[2] );
  668. gameLocal.Printf( "max clip sector is (%1.1f, %1.1f, %1.1f)\n", maxSector[0], maxSector[1], maxSector[2] );
  669. // initialize a default clip model
  670. defaultClipModel.LoadModel( idTraceModel( idBounds( idVec3( 0, 0, 0 ) ).Expand( 8 ) ) );
  671. // set counters to zero
  672. numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0;
  673. }
  674. /*
  675. ===============
  676. idClip::Shutdown
  677. ===============
  678. */
  679. void idClip::Shutdown() {
  680. delete[] clipSectors;
  681. clipSectors = NULL;
  682. // free the trace model used for the temporaryClipModel
  683. if ( temporaryClipModel.traceModelIndex != -1 ) {
  684. idClipModel::FreeTraceModel( temporaryClipModel.traceModelIndex );
  685. temporaryClipModel.traceModelIndex = -1;
  686. }
  687. // free the trace model used for the defaultClipModel
  688. if ( defaultClipModel.traceModelIndex != -1 ) {
  689. idClipModel::FreeTraceModel( defaultClipModel.traceModelIndex );
  690. defaultClipModel.traceModelIndex = -1;
  691. }
  692. clipLinkAllocator.Shutdown();
  693. }
  694. /*
  695. ====================
  696. idClip::ClipModelsTouchingBounds_r
  697. ====================
  698. */
  699. typedef struct listParms_s {
  700. idBounds bounds;
  701. int contentMask;
  702. idClipModel ** list;
  703. int count;
  704. int maxCount;
  705. } listParms_t;
  706. void idClip::ClipModelsTouchingBounds_r( const struct clipSector_s *node, listParms_t &parms ) const {
  707. while( node->axis != -1 ) {
  708. if ( parms.bounds[0][node->axis] > node->dist ) {
  709. node = node->children[0];
  710. } else if ( parms.bounds[1][node->axis] < node->dist ) {
  711. node = node->children[1];
  712. } else {
  713. ClipModelsTouchingBounds_r( node->children[0], parms );
  714. node = node->children[1];
  715. }
  716. }
  717. for ( clipLink_t *link = node->clipLinks; link; link = link->nextInSector ) {
  718. idClipModel *check = link->clipModel;
  719. // if the clip model is enabled
  720. if ( !check->enabled ) {
  721. continue;
  722. }
  723. // avoid duplicates in the list
  724. if ( check->touchCount == touchCount ) {
  725. continue;
  726. }
  727. // if the clip model does not have any contents we are looking for
  728. if ( !( check->contents & parms.contentMask ) ) {
  729. continue;
  730. }
  731. // if the bounds really do overlap
  732. if ( check->absBounds[0][0] > parms.bounds[1][0] ||
  733. check->absBounds[1][0] < parms.bounds[0][0] ||
  734. check->absBounds[0][1] > parms.bounds[1][1] ||
  735. check->absBounds[1][1] < parms.bounds[0][1] ||
  736. check->absBounds[0][2] > parms.bounds[1][2] ||
  737. check->absBounds[1][2] < parms.bounds[0][2] ) {
  738. continue;
  739. }
  740. if ( parms.count >= parms.maxCount ) {
  741. gameLocal.Warning( "idClip::ClipModelsTouchingBounds_r: max count" );
  742. return;
  743. }
  744. check->touchCount = touchCount;
  745. parms.list[parms.count] = check;
  746. parms.count++;
  747. }
  748. }
  749. /*
  750. ================
  751. idClip::ClipModelsTouchingBounds
  752. ================
  753. */
  754. int idClip::ClipModelsTouchingBounds( const idBounds &bounds, int contentMask, idClipModel **clipModelList, int maxCount ) const {
  755. listParms_t parms;
  756. if ( bounds[0][0] > bounds[1][0] ||
  757. bounds[0][1] > bounds[1][1] ||
  758. bounds[0][2] > bounds[1][2] ) {
  759. // we should not go through the tree for degenerate or backwards bounds
  760. assert( false );
  761. return 0;
  762. }
  763. parms.bounds[0] = bounds[0] - vec3_boxEpsilon;
  764. parms.bounds[1] = bounds[1] + vec3_boxEpsilon;
  765. parms.contentMask = contentMask;
  766. parms.list = clipModelList;
  767. parms.count = 0;
  768. parms.maxCount = maxCount;
  769. touchCount++;
  770. ClipModelsTouchingBounds_r( clipSectors, parms );
  771. return parms.count;
  772. }
  773. /*
  774. ================
  775. idClip::EntitiesTouchingBounds
  776. ================
  777. */
  778. int idClip::EntitiesTouchingBounds( const idBounds &bounds, int contentMask, idEntity **entityList, int maxCount ) const {
  779. idClipModel *clipModelList[MAX_GENTITIES];
  780. int i, j, count, entCount;
  781. count = idClip::ClipModelsTouchingBounds( bounds, contentMask, clipModelList, MAX_GENTITIES );
  782. entCount = 0;
  783. for ( i = 0; i < count; i++ ) {
  784. // entity could already be in the list because an entity can use multiple clip models
  785. for ( j = 0; j < entCount; j++ ) {
  786. if ( entityList[j] == clipModelList[i]->entity ) {
  787. break;
  788. }
  789. }
  790. if ( j >= entCount ) {
  791. if ( entCount >= maxCount ) {
  792. gameLocal.Warning( "idClip::EntitiesTouchingBounds: max count" );
  793. return entCount;
  794. }
  795. entityList[entCount] = clipModelList[i]->entity;
  796. entCount++;
  797. }
  798. }
  799. return entCount;
  800. }
  801. /*
  802. ====================
  803. idClip::GetTraceClipModels
  804. an ent will be excluded from testing if:
  805. cm->entity == passEntity ( don't clip against the pass entity )
  806. cm->entity == passOwner ( missiles don't clip with owner )
  807. cm->owner == passEntity ( don't interact with your own missiles )
  808. cm->owner == passOwner ( don't interact with other missiles from same owner )
  809. ====================
  810. */
  811. int idClip::GetTraceClipModels( const idBounds &bounds, int contentMask, const idEntity *passEntity, idClipModel **clipModelList ) const {
  812. int i, num;
  813. idClipModel *cm;
  814. idEntity *passOwner;
  815. num = ClipModelsTouchingBounds( bounds, contentMask, clipModelList, MAX_GENTITIES );
  816. if ( !passEntity ) {
  817. return num;
  818. }
  819. if ( passEntity->GetPhysics()->GetNumClipModels() > 0 ) {
  820. passOwner = passEntity->GetPhysics()->GetClipModel()->GetOwner();
  821. } else {
  822. passOwner = NULL;
  823. }
  824. for ( i = 0; i < num; i++ ) {
  825. cm = clipModelList[i];
  826. // check if we should ignore this entity
  827. if ( cm->entity == passEntity ) {
  828. clipModelList[i] = NULL; // don't clip against the pass entity
  829. } else if ( cm->entity == passOwner ) {
  830. clipModelList[i] = NULL; // missiles don't clip with their owner
  831. } else if ( cm->owner ) {
  832. if ( cm->owner == passEntity ) {
  833. clipModelList[i] = NULL; // don't clip against own missiles
  834. } else if ( cm->owner == passOwner ) {
  835. clipModelList[i] = NULL; // don't clip against other missiles from same owner
  836. }
  837. }
  838. }
  839. return num;
  840. }
  841. /*
  842. ============
  843. idClip::TraceRenderModel
  844. ============
  845. */
  846. void idClip::TraceRenderModel( trace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, const idMat3 &axis, idClipModel *touch ) const {
  847. trace.fraction = 1.0f;
  848. // if the trace is passing through the bounds
  849. if ( touch->absBounds.Expand( radius ).LineIntersection( start, end ) ) {
  850. modelTrace_t modelTrace;
  851. // test with exact render model and modify trace_t structure accordingly
  852. if ( gameRenderWorld->ModelTrace( modelTrace, touch->renderModelHandle, start, end, radius ) ) {
  853. trace.fraction = modelTrace.fraction;
  854. trace.endAxis = axis;
  855. trace.endpos = modelTrace.point;
  856. trace.c.normal = modelTrace.normal;
  857. trace.c.dist = modelTrace.point * modelTrace.normal;
  858. trace.c.point = modelTrace.point;
  859. trace.c.type = CONTACT_TRMVERTEX;
  860. trace.c.modelFeature = 0;
  861. trace.c.trmFeature = 0;
  862. trace.c.contents = modelTrace.material->GetContentFlags();
  863. trace.c.material = modelTrace.material;
  864. // NOTE: trace.c.id will be the joint number
  865. touch->id = JOINT_HANDLE_TO_CLIPMODEL_ID( modelTrace.jointNumber );
  866. }
  867. }
  868. }
  869. /*
  870. ============
  871. idClip::TraceModelForClipModel
  872. ============
  873. */
  874. const idTraceModel *idClip::TraceModelForClipModel( const idClipModel *mdl ) const {
  875. if ( !mdl ) {
  876. return NULL;
  877. } else {
  878. if ( !mdl->IsTraceModel() ) {
  879. if ( mdl->GetEntity() ) {
  880. gameLocal.Error( "TraceModelForClipModel: clip model %d on '%s' is not a trace model\n", mdl->GetId(), mdl->GetEntity()->name.c_str() );
  881. } else {
  882. gameLocal.Error( "TraceModelForClipModel: clip model %d is not a trace model\n", mdl->GetId() );
  883. }
  884. }
  885. return idClipModel::GetCachedTraceModel( mdl->traceModelIndex );
  886. }
  887. }
  888. /*
  889. ============
  890. idClip::TestHugeTranslation
  891. ============
  892. */
  893. ID_INLINE bool TestHugeTranslation( trace_t &results, const idClipModel *mdl, const idVec3 &start, const idVec3 &end, const idMat3 &trmAxis ) {
  894. if ( mdl != NULL && ( end - start ).LengthSqr() > Square( CM_MAX_TRACE_DIST ) ) {
  895. #ifndef CTF
  896. // May be important: This occurs in CTF when a player connects and spawns
  897. // in the PVS of a player that has a flag that is spawning the idMoveableItem
  898. // "nuggets". The error seems benign and the assert was getting in the way
  899. // of testing.
  900. assert( 0 );
  901. #endif
  902. results.fraction = 0.0f;
  903. results.endpos = start;
  904. results.endAxis = trmAxis;
  905. memset( &results.c, 0, sizeof( results.c ) );
  906. results.c.point = start;
  907. if ( mdl->GetEntity() ) {
  908. gameLocal.Printf( "huge translation for clip model %d on entity %d '%s'\n", mdl->GetId(), mdl->GetEntity()->entityNumber, mdl->GetEntity()->GetName() );
  909. } else {
  910. gameLocal.Printf( "huge translation for clip model %d\n", mdl->GetId() );
  911. }
  912. return true;
  913. }
  914. return false;
  915. }
  916. /*
  917. ============
  918. idClip::TranslationEntities
  919. ============
  920. */
  921. void idClip::TranslationEntities( trace_t &results, const idVec3 &start, const idVec3 &end,
  922. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  923. int i, num;
  924. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  925. idBounds traceBounds;
  926. float radius;
  927. trace_t trace;
  928. const idTraceModel *trm;
  929. if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) {
  930. return;
  931. }
  932. trm = TraceModelForClipModel( mdl );
  933. results.fraction = 1.0f;
  934. results.endpos = end;
  935. results.endAxis = trmAxis;
  936. if ( !trm ) {
  937. traceBounds.FromPointTranslation( start, end - start );
  938. radius = 0.0f;
  939. } else {
  940. traceBounds.FromBoundsTranslation( trm->bounds, start, trmAxis, end - start );
  941. radius = trm->bounds.GetRadius();
  942. }
  943. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  944. for ( i = 0; i < num; i++ ) {
  945. touch = clipModelList[i];
  946. if ( !touch ) {
  947. continue;
  948. }
  949. if ( touch->renderModelHandle != -1 ) {
  950. idClip::numRenderModelTraces++;
  951. TraceRenderModel( trace, start, end, radius, trmAxis, touch );
  952. } else {
  953. idClip::numTranslations++;
  954. collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask,
  955. touch->Handle(), touch->origin, touch->axis );
  956. }
  957. if ( trace.fraction < results.fraction ) {
  958. results = trace;
  959. results.c.entityNum = touch->entity->entityNumber;
  960. results.c.id = touch->id;
  961. if ( results.fraction == 0.0f ) {
  962. break;
  963. }
  964. }
  965. }
  966. }
  967. /*
  968. ============
  969. idClip::Translation
  970. ============
  971. */
  972. bool idClip::Translation( trace_t &results, const idVec3 &start, const idVec3 &end,
  973. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  974. int i, num;
  975. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  976. idBounds traceBounds;
  977. float radius;
  978. trace_t trace;
  979. const idTraceModel *trm;
  980. if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) {
  981. return true;
  982. }
  983. trm = TraceModelForClipModel( mdl );
  984. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  985. // test world
  986. idClip::numTranslations++;
  987. collisionModelManager->Translation( &results, start, end, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  988. results.c.entityNum = results.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
  989. if ( results.fraction == 0.0f ) {
  990. return true; // blocked immediately by the world
  991. }
  992. } else {
  993. memset( &results, 0, sizeof( results ) );
  994. results.fraction = 1.0f;
  995. results.endpos = end;
  996. results.endAxis = trmAxis;
  997. }
  998. if ( !trm ) {
  999. traceBounds.FromPointTranslation( start, results.endpos - start );
  1000. radius = 0.0f;
  1001. } else {
  1002. traceBounds.FromBoundsTranslation( trm->bounds, start, trmAxis, results.endpos - start );
  1003. radius = trm->bounds.GetRadius();
  1004. }
  1005. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  1006. for ( i = 0; i < num; i++ ) {
  1007. touch = clipModelList[i];
  1008. if ( !touch ) {
  1009. continue;
  1010. }
  1011. if ( touch->renderModelHandle != -1 ) {
  1012. idClip::numRenderModelTraces++;
  1013. TraceRenderModel( trace, start, end, radius, trmAxis, touch );
  1014. } else {
  1015. idClip::numTranslations++;
  1016. collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask,
  1017. touch->Handle(), touch->origin, touch->axis );
  1018. }
  1019. if ( trace.fraction < results.fraction ) {
  1020. results = trace;
  1021. results.c.entityNum = touch->entity->entityNumber;
  1022. results.c.id = touch->id;
  1023. if ( results.fraction == 0.0f ) {
  1024. break;
  1025. }
  1026. }
  1027. }
  1028. return ( results.fraction < 1.0f );
  1029. }
  1030. /*
  1031. ============
  1032. idClip::Rotation
  1033. ============
  1034. */
  1035. bool idClip::Rotation( trace_t &results, const idVec3 &start, const idRotation &rotation,
  1036. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  1037. int i, num;
  1038. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  1039. idBounds traceBounds;
  1040. trace_t trace;
  1041. const idTraceModel *trm;
  1042. trm = TraceModelForClipModel( mdl );
  1043. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  1044. // test world
  1045. idClip::numRotations++;
  1046. collisionModelManager->Rotation( &results, start, rotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  1047. results.c.entityNum = results.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
  1048. if ( results.fraction == 0.0f ) {
  1049. return true; // blocked immediately by the world
  1050. }
  1051. } else {
  1052. memset( &results, 0, sizeof( results ) );
  1053. results.fraction = 1.0f;
  1054. results.endpos = start;
  1055. results.endAxis = trmAxis * rotation.ToMat3();
  1056. }
  1057. if ( !trm ) {
  1058. traceBounds.FromPointRotation( start, rotation );
  1059. } else {
  1060. traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation );
  1061. }
  1062. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  1063. for ( i = 0; i < num; i++ ) {
  1064. touch = clipModelList[i];
  1065. if ( !touch ) {
  1066. continue;
  1067. }
  1068. // no rotational collision with render models
  1069. if ( touch->renderModelHandle != -1 ) {
  1070. continue;
  1071. }
  1072. idClip::numRotations++;
  1073. collisionModelManager->Rotation( &trace, start, rotation, trm, trmAxis, contentMask,
  1074. touch->Handle(), touch->origin, touch->axis );
  1075. if ( trace.fraction < results.fraction ) {
  1076. results = trace;
  1077. results.c.entityNum = touch->entity->entityNumber;
  1078. results.c.id = touch->id;
  1079. if ( results.fraction == 0.0f ) {
  1080. break;
  1081. }
  1082. }
  1083. }
  1084. return ( results.fraction < 1.0f );
  1085. }
  1086. /*
  1087. ============
  1088. idClip::Motion
  1089. ============
  1090. */
  1091. bool idClip::Motion( trace_t &results, const idVec3 &start, const idVec3 &end, const idRotation &rotation,
  1092. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  1093. int i, num;
  1094. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  1095. idVec3 dir, endPosition;
  1096. idBounds traceBounds;
  1097. float radius;
  1098. trace_t translationalTrace, rotationalTrace, trace;
  1099. idRotation endRotation;
  1100. const idTraceModel *trm;
  1101. assert( rotation.GetOrigin() == start );
  1102. if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) {
  1103. return true;
  1104. }
  1105. if ( mdl != NULL && rotation.GetAngle() != 0.0f && rotation.GetVec() != vec3_origin ) {
  1106. // if no translation
  1107. if ( start == end ) {
  1108. // pure rotation
  1109. return Rotation( results, start, rotation, mdl, trmAxis, contentMask, passEntity );
  1110. }
  1111. } else if ( start != end ) {
  1112. // pure translation
  1113. return Translation( results, start, end, mdl, trmAxis, contentMask, passEntity );
  1114. } else {
  1115. // no motion
  1116. results.fraction = 1.0f;
  1117. results.endpos = start;
  1118. results.endAxis = trmAxis;
  1119. return false;
  1120. }
  1121. trm = TraceModelForClipModel( mdl );
  1122. radius = trm->bounds.GetRadius();
  1123. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  1124. // translational collision with world
  1125. idClip::numTranslations++;
  1126. collisionModelManager->Translation( &translationalTrace, start, end, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  1127. translationalTrace.c.entityNum = translationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
  1128. } else {
  1129. memset( &translationalTrace, 0, sizeof( translationalTrace ) );
  1130. translationalTrace.fraction = 1.0f;
  1131. translationalTrace.endpos = end;
  1132. translationalTrace.endAxis = trmAxis;
  1133. }
  1134. if ( translationalTrace.fraction != 0.0f ) {
  1135. traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation );
  1136. dir = translationalTrace.endpos - start;
  1137. for ( i = 0; i < 3; i++ ) {
  1138. if ( dir[i] < 0.0f ) {
  1139. traceBounds[0][i] += dir[i];
  1140. }
  1141. else {
  1142. traceBounds[1][i] += dir[i];
  1143. }
  1144. }
  1145. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  1146. for ( i = 0; i < num; i++ ) {
  1147. touch = clipModelList[i];
  1148. if ( !touch ) {
  1149. continue;
  1150. }
  1151. if ( touch->renderModelHandle != -1 ) {
  1152. idClip::numRenderModelTraces++;
  1153. TraceRenderModel( trace, start, end, radius, trmAxis, touch );
  1154. } else {
  1155. idClip::numTranslations++;
  1156. collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask,
  1157. touch->Handle(), touch->origin, touch->axis );
  1158. }
  1159. if ( trace.fraction < translationalTrace.fraction ) {
  1160. translationalTrace = trace;
  1161. translationalTrace.c.entityNum = touch->entity->entityNumber;
  1162. translationalTrace.c.id = touch->id;
  1163. if ( translationalTrace.fraction == 0.0f ) {
  1164. break;
  1165. }
  1166. }
  1167. }
  1168. } else {
  1169. num = -1;
  1170. }
  1171. endPosition = translationalTrace.endpos;
  1172. endRotation = rotation;
  1173. endRotation.SetOrigin( endPosition );
  1174. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  1175. // rotational collision with world
  1176. idClip::numRotations++;
  1177. collisionModelManager->Rotation( &rotationalTrace, endPosition, endRotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  1178. rotationalTrace.c.entityNum = rotationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
  1179. } else {
  1180. memset( &rotationalTrace, 0, sizeof( rotationalTrace ) );
  1181. rotationalTrace.fraction = 1.0f;
  1182. rotationalTrace.endpos = endPosition;
  1183. rotationalTrace.endAxis = trmAxis * rotation.ToMat3();
  1184. }
  1185. if ( rotationalTrace.fraction != 0.0f ) {
  1186. if ( num == -1 ) {
  1187. traceBounds.FromBoundsRotation( trm->bounds, endPosition, trmAxis, endRotation );
  1188. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  1189. }
  1190. for ( i = 0; i < num; i++ ) {
  1191. touch = clipModelList[i];
  1192. if ( !touch ) {
  1193. continue;
  1194. }
  1195. // no rotational collision detection with render models
  1196. if ( touch->renderModelHandle != -1 ) {
  1197. continue;
  1198. }
  1199. idClip::numRotations++;
  1200. collisionModelManager->Rotation( &trace, endPosition, endRotation, trm, trmAxis, contentMask,
  1201. touch->Handle(), touch->origin, touch->axis );
  1202. if ( trace.fraction < rotationalTrace.fraction ) {
  1203. rotationalTrace = trace;
  1204. rotationalTrace.c.entityNum = touch->entity->entityNumber;
  1205. rotationalTrace.c.id = touch->id;
  1206. if ( rotationalTrace.fraction == 0.0f ) {
  1207. break;
  1208. }
  1209. }
  1210. }
  1211. }
  1212. if ( rotationalTrace.fraction < 1.0f ) {
  1213. results = rotationalTrace;
  1214. } else {
  1215. results = translationalTrace;
  1216. results.endAxis = rotationalTrace.endAxis;
  1217. }
  1218. results.fraction = Max( translationalTrace.fraction, rotationalTrace.fraction );
  1219. return ( translationalTrace.fraction < 1.0f || rotationalTrace.fraction < 1.0f );
  1220. }
  1221. /*
  1222. ============
  1223. idClip::Contacts
  1224. ============
  1225. */
  1226. int idClip::Contacts( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth,
  1227. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  1228. int i, j, num, n, numContacts;
  1229. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  1230. idBounds traceBounds;
  1231. const idTraceModel *trm;
  1232. trm = TraceModelForClipModel( mdl );
  1233. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  1234. // test world
  1235. idClip::numContacts++;
  1236. numContacts = collisionModelManager->Contacts( contacts, maxContacts, start, dir, depth, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  1237. } else {
  1238. numContacts = 0;
  1239. }
  1240. for ( i = 0; i < numContacts; i++ ) {
  1241. contacts[i].entityNum = ENTITYNUM_WORLD;
  1242. contacts[i].id = 0;
  1243. }
  1244. if ( numContacts >= maxContacts ) {
  1245. return numContacts;
  1246. }
  1247. if ( !trm ) {
  1248. traceBounds = idBounds( start ).Expand( depth );
  1249. } else {
  1250. traceBounds.FromTransformedBounds( trm->bounds, start, trmAxis );
  1251. traceBounds.ExpandSelf( depth );
  1252. }
  1253. num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList );
  1254. for ( i = 0; i < num; i++ ) {
  1255. touch = clipModelList[i];
  1256. if ( !touch ) {
  1257. continue;
  1258. }
  1259. // no contacts with render models
  1260. if ( touch->renderModelHandle != -1 ) {
  1261. continue;
  1262. }
  1263. idClip::numContacts++;
  1264. n = collisionModelManager->Contacts( contacts + numContacts, maxContacts - numContacts,
  1265. start, dir, depth, trm, trmAxis, contentMask,
  1266. touch->Handle(), touch->origin, touch->axis );
  1267. for ( j = 0; j < n; j++ ) {
  1268. contacts[numContacts].entityNum = touch->entity->entityNumber;
  1269. contacts[numContacts].id = touch->id;
  1270. numContacts++;
  1271. }
  1272. if ( numContacts >= maxContacts ) {
  1273. break;
  1274. }
  1275. }
  1276. return numContacts;
  1277. }
  1278. /*
  1279. ============
  1280. idClip::Contents
  1281. ============
  1282. */
  1283. int idClip::Contents( const idVec3 &start, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
  1284. int i, num, contents;
  1285. idClipModel *touch, *clipModelList[MAX_GENTITIES];
  1286. idBounds traceBounds;
  1287. const idTraceModel *trm;
  1288. trm = TraceModelForClipModel( mdl );
  1289. if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) {
  1290. // test world
  1291. idClip::numContents++;
  1292. contents = collisionModelManager->Contents( start, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default );
  1293. } else {
  1294. contents = 0;
  1295. }
  1296. if ( !trm ) {
  1297. traceBounds[0] = start;
  1298. traceBounds[1] = start;
  1299. } else if ( trmAxis.IsRotated() ) {
  1300. traceBounds.FromTransformedBounds( trm->bounds, start, trmAxis );
  1301. } else {
  1302. traceBounds[0] = trm->bounds[0] + start;
  1303. traceBounds[1] = trm->bounds[1] + start;
  1304. }
  1305. num = GetTraceClipModels( traceBounds, -1, passEntity, clipModelList );
  1306. for ( i = 0; i < num; i++ ) {
  1307. touch = clipModelList[i];
  1308. if ( !touch ) {
  1309. continue;
  1310. }
  1311. // no contents test with render models
  1312. if ( touch->renderModelHandle != -1 ) {
  1313. continue;
  1314. }
  1315. // if the entity does not have any contents we are looking for
  1316. if ( ( touch->contents & contentMask ) == 0 ) {
  1317. continue;
  1318. }
  1319. // if the entity has no new contents flags
  1320. if ( ( touch->contents & contents ) == touch->contents ) {
  1321. continue;
  1322. }
  1323. idClip::numContents++;
  1324. if ( collisionModelManager->Contents( start, trm, trmAxis, contentMask, touch->Handle(), touch->origin, touch->axis ) ) {
  1325. contents |= ( touch->contents & contentMask );
  1326. }
  1327. }
  1328. return contents;
  1329. }
  1330. /*
  1331. ============
  1332. idClip::TranslationModel
  1333. ============
  1334. */
  1335. void idClip::TranslationModel( trace_t &results, const idVec3 &start, const idVec3 &end,
  1336. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask,
  1337. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1338. const idTraceModel *trm = TraceModelForClipModel( mdl );
  1339. idClip::numTranslations++;
  1340. collisionModelManager->Translation( &results, start, end, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1341. }
  1342. /*
  1343. ============
  1344. idClip::RotationModel
  1345. ============
  1346. */
  1347. void idClip::RotationModel( trace_t &results, const idVec3 &start, const idRotation &rotation,
  1348. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask,
  1349. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1350. const idTraceModel *trm = TraceModelForClipModel( mdl );
  1351. idClip::numRotations++;
  1352. collisionModelManager->Rotation( &results, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1353. }
  1354. /*
  1355. ============
  1356. idClip::ContactsModel
  1357. ============
  1358. */
  1359. int idClip::ContactsModel( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth,
  1360. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask,
  1361. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1362. const idTraceModel *trm = TraceModelForClipModel( mdl );
  1363. idClip::numContacts++;
  1364. return collisionModelManager->Contacts( contacts, maxContacts, start, dir, depth, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1365. }
  1366. /*
  1367. ============
  1368. idClip::ContentsModel
  1369. ============
  1370. */
  1371. int idClip::ContentsModel( const idVec3 &start,
  1372. const idClipModel *mdl, const idMat3 &trmAxis, int contentMask,
  1373. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1374. const idTraceModel *trm = TraceModelForClipModel( mdl );
  1375. idClip::numContents++;
  1376. return collisionModelManager->Contents( start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1377. }
  1378. /*
  1379. ============
  1380. idClip::GetModelContactFeature
  1381. ============
  1382. */
  1383. bool idClip::GetModelContactFeature( const contactInfo_t &contact, const idClipModel *clipModel, idFixedWinding &winding ) const {
  1384. int i;
  1385. cmHandle_t handle;
  1386. idVec3 start, end;
  1387. handle = -1;
  1388. winding.Clear();
  1389. if ( clipModel == NULL ) {
  1390. handle = 0;
  1391. } else {
  1392. if ( clipModel->renderModelHandle != -1 ) {
  1393. winding += contact.point;
  1394. return true;
  1395. } else if ( clipModel->traceModelIndex != -1 ) {
  1396. handle = collisionModelManager->SetupTrmModel( *idClipModel::GetCachedTraceModel( clipModel->traceModelIndex ), clipModel->material );
  1397. } else {
  1398. handle = clipModel->collisionModelHandle;
  1399. }
  1400. }
  1401. // if contact with a collision model
  1402. if ( handle != -1 ) {
  1403. switch( contact.type ) {
  1404. case CONTACT_EDGE: {
  1405. // the model contact feature is a collision model edge
  1406. collisionModelManager->GetModelEdge( handle, contact.modelFeature, start, end );
  1407. winding += start;
  1408. winding += end;
  1409. break;
  1410. }
  1411. case CONTACT_MODELVERTEX: {
  1412. // the model contact feature is a collision model vertex
  1413. collisionModelManager->GetModelVertex( handle, contact.modelFeature, start );
  1414. winding += start;
  1415. break;
  1416. }
  1417. case CONTACT_TRMVERTEX: {
  1418. // the model contact feature is a collision model polygon
  1419. collisionModelManager->GetModelPolygon( handle, contact.modelFeature, winding );
  1420. break;
  1421. }
  1422. }
  1423. }
  1424. // transform the winding to world space
  1425. if ( clipModel ) {
  1426. for ( i = 0; i < winding.GetNumPoints(); i++ ) {
  1427. winding[i].ToVec3() *= clipModel->axis;
  1428. winding[i].ToVec3() += clipModel->origin;
  1429. }
  1430. }
  1431. return true;
  1432. }
  1433. /*
  1434. ============
  1435. idClip::PrintStatistics
  1436. ============
  1437. */
  1438. void idClip::PrintStatistics() {
  1439. gameLocal.Printf( "t = %-3d, r = %-3d, m = %-3d, render = %-3d, contents = %-3d, contacts = %-3d\n",
  1440. numTranslations, numRotations, numMotions, numRenderModelTraces, numContents, numContacts );
  1441. numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0;
  1442. }
  1443. /*
  1444. ============
  1445. idClip::DrawClipModels
  1446. ============
  1447. */
  1448. void idClip::DrawClipModels( const idVec3 &eye, const float radius, const idEntity *passEntity ) {
  1449. int i, num;
  1450. idBounds bounds;
  1451. idClipModel *clipModelList[MAX_GENTITIES];
  1452. idClipModel *clipModel;
  1453. bounds = idBounds( eye ).Expand( radius );
  1454. num = idClip::ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
  1455. for ( i = 0; i < num; i++ ) {
  1456. clipModel = clipModelList[i];
  1457. if ( clipModel->GetEntity() == passEntity ) {
  1458. continue;
  1459. }
  1460. if ( clipModel->renderModelHandle != -1 ) {
  1461. gameRenderWorld->DebugBounds( colorCyan, clipModel->GetAbsBounds() );
  1462. } else {
  1463. collisionModelManager->DrawModel( clipModel->Handle(), clipModel->GetOrigin(), clipModel->GetAxis(), eye, radius );
  1464. }
  1465. }
  1466. }
  1467. /*
  1468. ============
  1469. idClip::DrawModelContactFeature
  1470. ============
  1471. */
  1472. bool idClip::DrawModelContactFeature( const contactInfo_t &contact, const idClipModel *clipModel, int lifetime ) const {
  1473. int i;
  1474. idMat3 axis;
  1475. idFixedWinding winding;
  1476. if ( !GetModelContactFeature( contact, clipModel, winding ) ) {
  1477. return false;
  1478. }
  1479. axis = contact.normal.ToMat3();
  1480. if ( winding.GetNumPoints() == 1 ) {
  1481. gameRenderWorld->DebugLine( colorCyan, winding[0].ToVec3(), winding[0].ToVec3() + 2.0f * axis[0], lifetime );
  1482. gameRenderWorld->DebugLine( colorWhite, winding[0].ToVec3() - 1.0f * axis[1], winding[0].ToVec3() + 1.0f * axis[1], lifetime );
  1483. gameRenderWorld->DebugLine( colorWhite, winding[0].ToVec3() - 1.0f * axis[2], winding[0].ToVec3() + 1.0f * axis[2], lifetime );
  1484. } else {
  1485. for ( i = 0; i < winding.GetNumPoints(); i++ ) {
  1486. gameRenderWorld->DebugLine( colorCyan, winding[i].ToVec3(), winding[(i+1)%winding.GetNumPoints()].ToVec3(), lifetime );
  1487. }
  1488. }
  1489. axis[0] = -axis[0];
  1490. axis[2] = -axis[2];
  1491. gameRenderWorld->DrawText( contact.material->GetName(), winding.GetCenter() - 4.0f * axis[2], 0.1f, colorWhite, axis, 1, 5000 );
  1492. return true;
  1493. }