Clip.cpp 46 KB

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