BrittleFracture.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387
  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. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. CLASS_DECLARATION( idEntity, idBrittleFracture )
  24. EVENT( EV_Activate, idBrittleFracture::Event_Activate )
  25. EVENT( EV_Touch, idBrittleFracture::Event_Touch )
  26. END_CLASS
  27. const int SHARD_ALIVE_TIME = 5000;
  28. const int SHARD_FADE_START = 2000;
  29. static const char *brittleFracture_SnapshotName = "_BrittleFracture_Snapshot_";
  30. /*
  31. ================
  32. idBrittleFracture::idBrittleFracture
  33. ================
  34. */
  35. idBrittleFracture::idBrittleFracture() {
  36. material = NULL;
  37. decalMaterial = NULL;
  38. decalSize = 0.0f;
  39. maxShardArea = 0.0f;
  40. maxShatterRadius = 0.0f;
  41. minShatterRadius = 0.0f;
  42. linearVelocityScale = 0.0f;
  43. angularVelocityScale = 0.0f;
  44. shardMass = 0.0f;
  45. density = 0.0f;
  46. friction = 0.0f;
  47. bouncyness = 0.0f;
  48. fxFracture.Clear();
  49. bounds.Clear();
  50. disableFracture = false;
  51. lastRenderEntityUpdate = -1;
  52. changed = false;
  53. fl.networkSync = true;
  54. isXraySurface = false;
  55. }
  56. /*
  57. ================
  58. idBrittleFracture::~idBrittleFracture
  59. ================
  60. */
  61. idBrittleFracture::~idBrittleFracture() {
  62. int i;
  63. for ( i = 0; i < shards.Num(); i++ ) {
  64. shards[i]->decals.DeleteContents( true );
  65. delete shards[i];
  66. }
  67. // make sure the render entity is freed before the model is freed
  68. FreeModelDef();
  69. renderModelManager->FreeModel( renderEntity.hModel );
  70. // Free our events list memory
  71. storedEvents.Clear();
  72. }
  73. /*
  74. ================
  75. idBrittleFracture::Save
  76. ================
  77. */
  78. void idBrittleFracture::Save( idSaveGame *savefile ) const {
  79. savefile->WriteInt( health );
  80. entityFlags_s flags = fl;
  81. LittleBitField( &flags, sizeof( flags ) );
  82. savefile->Write( &flags, sizeof( flags ) );
  83. // setttings
  84. savefile->WriteMaterial( material );
  85. savefile->WriteMaterial( decalMaterial );
  86. savefile->WriteFloat( decalSize );
  87. savefile->WriteFloat( maxShardArea );
  88. savefile->WriteFloat( maxShatterRadius );
  89. savefile->WriteFloat( minShatterRadius );
  90. savefile->WriteFloat( linearVelocityScale );
  91. savefile->WriteFloat( angularVelocityScale );
  92. savefile->WriteFloat( shardMass );
  93. savefile->WriteFloat( density );
  94. savefile->WriteFloat( friction );
  95. savefile->WriteFloat( bouncyness );
  96. savefile->WriteString( fxFracture );
  97. // state
  98. savefile->WriteBounds( bounds );
  99. savefile->WriteBool( disableFracture );
  100. savefile->WriteInt( lastRenderEntityUpdate );
  101. savefile->WriteBool( changed );
  102. savefile->WriteModel( defaultRenderModel );
  103. // So we can re-break the object on load if needed
  104. savefile->WriteInt( storedEvents.Num() );
  105. for ( int i = 0; i < storedEvents.Num(); ++i ) {
  106. savefile->WriteInt( storedEvents[i].eventType );
  107. savefile->WriteVec3( storedEvents[i].point );
  108. savefile->WriteVec3( storedEvents[i].vector );
  109. }
  110. savefile->WriteBool( isXraySurface );
  111. }
  112. /*
  113. ================
  114. idBrittleFracture::Restore
  115. ================
  116. */
  117. void idBrittleFracture::Restore( idRestoreGame *savefile ) {
  118. savefile->ReadInt( health );
  119. savefile->Read( &fl, sizeof( fl ) );
  120. LittleBitField( &fl, sizeof( fl ) );
  121. // setttings
  122. savefile->ReadMaterial( material );
  123. savefile->ReadMaterial( decalMaterial );
  124. savefile->ReadFloat( decalSize );
  125. savefile->ReadFloat( maxShardArea );
  126. savefile->ReadFloat( maxShatterRadius );
  127. savefile->ReadFloat( minShatterRadius );
  128. savefile->ReadFloat( linearVelocityScale );
  129. savefile->ReadFloat( angularVelocityScale );
  130. savefile->ReadFloat( shardMass );
  131. savefile->ReadFloat( density );
  132. savefile->ReadFloat( friction );
  133. savefile->ReadFloat( bouncyness );
  134. savefile->ReadString( fxFracture );
  135. // state
  136. savefile->ReadBounds(bounds);
  137. savefile->ReadBool( disableFracture );
  138. savefile->ReadInt( lastRenderEntityUpdate );
  139. savefile->ReadBool( changed );
  140. savefile->ReadModel( defaultRenderModel );
  141. // Reset all brittle Fractures so we can re-break them if necessary
  142. fl.takedamage = true;
  143. CreateFractures( defaultRenderModel );
  144. FindNeighbours();
  145. int numEvents = 0;
  146. bool resolveBreaks = false;
  147. savefile->ReadInt( numEvents );
  148. for( int i = 0; i < numEvents; i++ ) {
  149. fractureEvent_s restoredEvent;
  150. savefile->ReadInt( restoredEvent.eventType );
  151. savefile->ReadVec3( restoredEvent.point );
  152. savefile->ReadVec3( restoredEvent.vector );
  153. if ( restoredEvent.eventType == EVENT_PROJECT_DECAL ) {
  154. ProjectDecal( restoredEvent.point, restoredEvent.vector, gameLocal.time, NULL );
  155. } else {
  156. Shatter( restoredEvent.point, restoredEvent.vector, gameLocal.time );
  157. }
  158. resolveBreaks = true;
  159. }
  160. // remove any dropped shards
  161. for ( int i = 0; resolveBreaks && i < shards.Num(); i++ ) {
  162. if ( shards[i]->droppedTime!= -1 ) {
  163. RemoveShard( i );
  164. i--;
  165. }
  166. }
  167. renderEntity.hModel = renderModelManager->AllocModel();
  168. renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
  169. renderEntity.callback = idBrittleFracture::ModelCallback;
  170. renderEntity.noShadow = true;
  171. renderEntity.noSelfShadow = true;
  172. renderEntity.noDynamicInteractions = false;
  173. savefile->ReadBool( isXraySurface );
  174. }
  175. /*
  176. ================
  177. idBrittleFracture::Spawn
  178. ================
  179. */
  180. void idBrittleFracture::Spawn() {
  181. // get shard properties
  182. decalMaterial = declManager->FindMaterial( spawnArgs.GetString( "mtr_decal" ) );
  183. decalSize = spawnArgs.GetFloat( "decalSize", "40" );
  184. maxShardArea = spawnArgs.GetFloat( "maxShardArea", "200" ) * 2.0f ;
  185. maxShardArea = idMath::ClampFloat( 100, 10000, maxShardArea );
  186. maxShatterRadius = spawnArgs.GetFloat( "maxShatterRadius", "40" );
  187. minShatterRadius = spawnArgs.GetFloat( "minShatterRadius", "10" );
  188. linearVelocityScale = spawnArgs.GetFloat( "linearVelocityScale", "0.1" );
  189. angularVelocityScale = spawnArgs.GetFloat( "angularVelocityScale", "40" );
  190. fxFracture = spawnArgs.GetString( "fx" );
  191. // make sure that max is greater than min ( otherwise negative number square root happens )
  192. if( maxShatterRadius < minShatterRadius ) {
  193. idLib::Warning( "BrittleFracture, minShatterRadius(%2f) is greater than maxShatterRadius(%2f). Unknown results will ensue.", minShatterRadius, maxShatterRadius );
  194. }
  195. // get rigid body properties
  196. shardMass = spawnArgs.GetFloat( "shardMass", "20" );
  197. shardMass = idMath::ClampFloat( 0.001f, 1000.0f, shardMass );
  198. spawnArgs.GetFloat( "density", "0.1", density );
  199. density = idMath::ClampFloat( 0.001f, 1000.0f, density );
  200. spawnArgs.GetFloat( "friction", "0.4", friction );
  201. friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
  202. spawnArgs.GetFloat( "bouncyness", "0.01", bouncyness );
  203. bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
  204. disableFracture = spawnArgs.GetBool( "disableFracture", "0" );
  205. health = spawnArgs.GetInt( "health", "40" );
  206. fl.takedamage = true;
  207. // FIXME: set "bleed" so idProjectile calls AddDamageEffect
  208. spawnArgs.SetBool( "bleed", 1 );
  209. // check for xray surface
  210. if ( renderEntity.hModel != NULL ) {
  211. const idRenderModel *model = renderEntity.hModel;
  212. isXraySurface = false;
  213. for ( int i = 0; i < model->NumSurfaces(); i++ ) {
  214. const modelSurface_t *surf = model->Surface( i );
  215. if ( idStr( surf->shader->GetName() ) == "textures/smf/window_scratch" ) {
  216. isXraySurface = true;
  217. break;
  218. }
  219. }
  220. }
  221. CreateFractures( renderEntity.hModel );
  222. FindNeighbours();
  223. defaultRenderModel = renderEntity.hModel;
  224. renderEntity.hModel = renderModelManager->AllocModel();
  225. renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
  226. renderEntity.callback = idBrittleFracture::ModelCallback;
  227. renderEntity.noShadow = true;
  228. renderEntity.noSelfShadow = true;
  229. renderEntity.noDynamicInteractions = false;
  230. }
  231. /*
  232. ================
  233. idBrittleFracture::AddShard
  234. ================
  235. */
  236. void idBrittleFracture::AddShard( idClipModel *clipModel, idFixedWinding &w ) {
  237. shard_t *shard = new (TAG_PARTICLE) shard_t;
  238. shard->clipModel = clipModel;
  239. shard->droppedTime = -1;
  240. shard->winding = w;
  241. shard->decals.Clear();
  242. shard->edgeHasNeighbour.AssureSize( w.GetNumPoints(), false );
  243. shard->neighbours.Clear();
  244. shard->atEdge = false;
  245. shards.Append( shard );
  246. }
  247. /*
  248. ================
  249. idBrittleFracture::RemoveShard
  250. ================
  251. */
  252. void idBrittleFracture::RemoveShard( int index ) {
  253. int i;
  254. delete shards[index];
  255. shards.RemoveIndex( index );
  256. physicsObj.RemoveIndex( index );
  257. for ( i = index; i < shards.Num(); i++ ) {
  258. shards[i]->clipModel->SetId( i );
  259. }
  260. }
  261. /*
  262. ================
  263. idBrittleFracture::UpdateRenderEntity
  264. ================
  265. */
  266. bool idBrittleFracture::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
  267. int i, j, k, n, msec, numTris, numDecalTris;
  268. float fade;
  269. dword packedColor;
  270. srfTriangles_t *tris, *decalTris;
  271. modelSurface_t surface;
  272. idDrawVert *v;
  273. idPlane plane;
  274. idMat3 tangents;
  275. // this may be triggered by a model trace or other non-view related source,
  276. // to which we should look like an empty model
  277. if ( !renderView ) {
  278. return false;
  279. }
  280. // don't regenerate it if it is current
  281. if ( lastRenderEntityUpdate == gameLocal.time || !changed ) {
  282. return false;
  283. }
  284. lastRenderEntityUpdate = gameLocal.time;
  285. changed = false;
  286. numTris = 0;
  287. numDecalTris = 0;
  288. for ( i = 0; i < shards.Num(); i++ ) {
  289. n = shards[i]->winding.GetNumPoints();
  290. if ( n > 2 ) {
  291. numTris += n - 2;
  292. }
  293. for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
  294. n = shards[i]->decals[k]->GetNumPoints();
  295. if ( n > 2 ) {
  296. numDecalTris += n - 2;
  297. }
  298. }
  299. }
  300. // FIXME: re-use model surfaces
  301. renderEntity->hModel->InitEmpty( brittleFracture_SnapshotName );
  302. // allocate triangle surfaces for the fractures and decals
  303. tris = renderEntity->hModel->AllocSurfaceTriangles( numTris * 3, material->ShouldCreateBackSides() ? numTris * 6 : numTris * 3 );
  304. decalTris = renderEntity->hModel->AllocSurfaceTriangles( numDecalTris * 3, decalMaterial->ShouldCreateBackSides() ? numDecalTris * 6 : numDecalTris * 3 );
  305. for ( i = 0; i < shards.Num(); i++ ) {
  306. const idVec3 &origin = shards[i]->clipModel->GetOrigin();
  307. const idMat3 &axis = shards[i]->clipModel->GetAxis();
  308. fade = 1.0f;
  309. if ( shards[i]->droppedTime >= 0 ) {
  310. msec = gameLocal.time - shards[i]->droppedTime - SHARD_FADE_START;
  311. if ( msec > 0 ) {
  312. fade = 1.0f - (float) msec / ( SHARD_ALIVE_TIME - SHARD_FADE_START );
  313. }
  314. }
  315. packedColor = PackColor( idVec4( renderEntity->shaderParms[ SHADERPARM_RED ] * fade,
  316. renderEntity->shaderParms[ SHADERPARM_GREEN ] * fade,
  317. renderEntity->shaderParms[ SHADERPARM_BLUE ] * fade,
  318. fade ) );
  319. const idWinding &winding = shards[i]->winding;
  320. winding.GetPlane( plane );
  321. tangents = ( plane.Normal() * axis ).ToMat3();
  322. for ( j = 2; j < winding.GetNumPoints(); j++ ) {
  323. v = &tris->verts[tris->numVerts++];
  324. v->Clear();
  325. v->xyz = origin + winding[0].ToVec3() * axis;
  326. v->SetTexCoord( winding[0].s, winding[0].t );
  327. v->SetNormal( tangents[0] );
  328. v->SetTangent( tangents[1] );
  329. v->SetBiTangent( tangents[2] );
  330. v->SetColor( packedColor );
  331. v = &tris->verts[tris->numVerts++];
  332. v->Clear();
  333. v->xyz = origin + winding[j-1].ToVec3() * axis;
  334. v->SetTexCoord( winding[j-1].s, winding[j-1].t );
  335. v->SetNormal( tangents[0] );
  336. v->SetTangent( tangents[1] );
  337. v->SetBiTangent( tangents[2] );
  338. v->SetColor( packedColor );
  339. v = &tris->verts[tris->numVerts++];
  340. v->Clear();
  341. v->xyz = origin + winding[j].ToVec3() * axis;
  342. v->SetTexCoord( winding[j].s, winding[j].t );
  343. v->SetNormal( tangents[0] );
  344. v->SetTangent( tangents[1] );
  345. v->SetBiTangent( tangents[2] );
  346. v->SetColor( packedColor );
  347. tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
  348. tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
  349. tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
  350. if ( material->ShouldCreateBackSides() ) {
  351. tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
  352. tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
  353. tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
  354. }
  355. }
  356. for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
  357. const idWinding &decalWinding = *shards[i]->decals[k];
  358. for ( j = 2; j < decalWinding.GetNumPoints(); j++ ) {
  359. v = &decalTris->verts[decalTris->numVerts++];
  360. v->Clear();
  361. v->xyz = origin + decalWinding[0].ToVec3() * axis;
  362. v->SetTexCoord( decalWinding[0].s, decalWinding[0].t );
  363. v->SetNormal( tangents[0] );
  364. v->SetTangent( tangents[1] );
  365. v->SetBiTangent( tangents[2] );
  366. v->SetColor( packedColor );
  367. v = &decalTris->verts[decalTris->numVerts++];
  368. v->Clear();
  369. v->xyz = origin + decalWinding[j-1].ToVec3() * axis;
  370. v->SetTexCoord( decalWinding[j-1].s, decalWinding[j-1].t );
  371. v->SetNormal( tangents[0] );
  372. v->SetTangent( tangents[1] );
  373. v->SetBiTangent( tangents[2] );
  374. v->SetColor( packedColor );
  375. v = &decalTris->verts[decalTris->numVerts++];
  376. v->Clear();
  377. v->xyz = origin + decalWinding[j].ToVec3() * axis;
  378. v->SetTexCoord( decalWinding[j].s, decalWinding[j].t );
  379. v->SetNormal( tangents[0] );
  380. v->SetTangent( tangents[1] );
  381. v->SetBiTangent( tangents[2] );
  382. v->SetColor( packedColor );
  383. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
  384. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
  385. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
  386. if ( decalMaterial->ShouldCreateBackSides() ) {
  387. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
  388. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
  389. decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
  390. }
  391. }
  392. }
  393. }
  394. tris->tangentsCalculated = true;
  395. decalTris->tangentsCalculated = true;
  396. SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts );
  397. SIMDProcessor->MinMax( decalTris->bounds[0], decalTris->bounds[1], decalTris->verts, decalTris->numVerts );
  398. memset( &surface, 0, sizeof( surface ) );
  399. surface.shader = material;
  400. surface.id = 0;
  401. surface.geometry = tris;
  402. renderEntity->hModel->AddSurface( surface );
  403. memset( &surface, 0, sizeof( surface ) );
  404. surface.shader = decalMaterial;
  405. surface.id = 1;
  406. surface.geometry = decalTris;
  407. renderEntity->hModel->AddSurface( surface );
  408. return true;
  409. }
  410. /*
  411. ================
  412. idBrittleFracture::ModelCallback
  413. ================
  414. */
  415. bool idBrittleFracture::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
  416. const idBrittleFracture *ent;
  417. ent = static_cast<idBrittleFracture *>(gameLocal.entities[ renderEntity->entityNum ]);
  418. if ( ent == NULL ) {
  419. gameLocal.Error( "idBrittleFracture::ModelCallback: callback with NULL game entity" );
  420. return false;
  421. }
  422. return ent->UpdateRenderEntity( renderEntity, renderView );
  423. }
  424. /*
  425. ================
  426. idBrittleFracture::Present
  427. ================
  428. */
  429. void idBrittleFracture::Present() {
  430. // don't present to the renderer if the entity hasn't changed
  431. if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
  432. return;
  433. }
  434. BecomeInactive( TH_UPDATEVISUALS );
  435. renderEntity.bounds = bounds;
  436. renderEntity.origin.Zero();
  437. renderEntity.axis.Identity();
  438. // force an update because the bounds/origin/axis may stay the same while the model changes
  439. renderEntity.forceUpdate = true;
  440. // add to refresh list
  441. if ( modelDefHandle == -1 ) {
  442. modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
  443. } else {
  444. gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
  445. }
  446. changed = true;
  447. }
  448. /*
  449. ================
  450. idBrittleFracture::Think
  451. ================
  452. */
  453. void idBrittleFracture::Think() {
  454. int i, startTime, endTime, droppedTime;
  455. shard_t *shard;
  456. bool atRest = true, fading = false;
  457. // remove overdue shards
  458. for ( i = 0; i < shards.Num(); i++ ) {
  459. droppedTime = shards[i]->droppedTime;
  460. if ( droppedTime != -1 ) {
  461. if ( gameLocal.time - droppedTime > SHARD_ALIVE_TIME ) {
  462. RemoveShard( i );
  463. i--;
  464. }
  465. fading = true;
  466. }
  467. }
  468. // remove the entity when nothing is visible
  469. if ( !shards.Num() ) {
  470. PostEventMS( &EV_Remove, 0 );
  471. return;
  472. }
  473. if ( thinkFlags & TH_PHYSICS ) {
  474. startTime = gameLocal.previousTime;
  475. endTime = gameLocal.time;
  476. // run physics on shards
  477. for ( i = 0; i < shards.Num(); i++ ) {
  478. shard = shards[i];
  479. if ( shard->droppedTime == -1 ) {
  480. continue;
  481. }
  482. shard->physicsObj.Evaluate( endTime - startTime, endTime );
  483. if ( !shard->physicsObj.IsAtRest() ) {
  484. atRest = false;
  485. }
  486. }
  487. if ( atRest ) {
  488. BecomeInactive( TH_PHYSICS );
  489. } else {
  490. BecomeActive( TH_PHYSICS );
  491. }
  492. }
  493. if ( !atRest || bounds.IsCleared() ) {
  494. bounds.Clear();
  495. for ( i = 0; i < shards.Num(); i++ ) {
  496. bounds.AddBounds( shards[i]->clipModel->GetAbsBounds() );
  497. }
  498. }
  499. if ( fading ) {
  500. BecomeActive( TH_UPDATEVISUALS | TH_THINK );
  501. } else {
  502. BecomeInactive( TH_THINK );
  503. }
  504. RunPhysics();
  505. Present();
  506. }
  507. /*
  508. ================
  509. idBrittleFracture::ApplyImpulse
  510. ================
  511. */
  512. void idBrittleFracture::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
  513. if ( id < 0 || id >= shards.Num() ) {
  514. return;
  515. }
  516. if ( shards[id]->droppedTime != -1 ) {
  517. shards[id]->physicsObj.ApplyImpulse( 0, point, impulse );
  518. } else if ( health <= 0 && !disableFracture ) {
  519. Shatter( point, impulse, gameLocal.time );
  520. }
  521. }
  522. /*
  523. ================
  524. idBrittleFracture::AddForce
  525. ================
  526. */
  527. void idBrittleFracture::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
  528. if ( id < 0 || id >= shards.Num() ) {
  529. return;
  530. }
  531. if ( shards[id]->droppedTime != -1 ) {
  532. shards[id]->physicsObj.AddForce( 0, point, force );
  533. } else if ( health <= 0 && !disableFracture ) {
  534. Shatter( point, force, gameLocal.time );
  535. }
  536. }
  537. /*
  538. ================
  539. idBrittleFracture::ProjectDecal
  540. ================
  541. */
  542. void idBrittleFracture::ProjectDecal( const idVec3 &point, const idVec3 &dir, const int time, const char *damageDefName ) {
  543. int i, j, bits, clipBits;
  544. float a, c, s;
  545. idVec2 st[MAX_POINTS_ON_WINDING];
  546. idVec3 origin;
  547. idMat3 axis, axistemp;
  548. idPlane textureAxis[2];
  549. if ( common->IsServer() ) {
  550. idBitMsg msg;
  551. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  552. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  553. msg.BeginWriting();
  554. msg.WriteFloat( point[0] );
  555. msg.WriteFloat( point[1] );
  556. msg.WriteFloat( point[2] );
  557. msg.WriteFloat( dir[0] );
  558. msg.WriteFloat( dir[1] );
  559. msg.WriteFloat( dir[2] );
  560. ServerSendEvent( EVENT_PROJECT_DECAL, &msg, true );
  561. }
  562. // store the event so we can rebuilt the fracture after loading a save
  563. fractureEvent_s fractureEvent;
  564. fractureEvent.eventType = EVENT_PROJECT_DECAL;
  565. fractureEvent.point = point;
  566. fractureEvent.vector = dir;
  567. storedEvents.Append( fractureEvent );
  568. if ( time >= gameLocal.time ) {
  569. // try to get the sound from the damage def
  570. const idDeclEntityDef *damageDef = NULL;
  571. const idSoundShader *sndShader = NULL;
  572. if ( damageDefName ) {
  573. damageDef = gameLocal.FindEntityDef( damageDefName, false );
  574. if ( damageDef ) {
  575. const char * sndName = damageDef->dict.GetString( "snd_shatter", "" );
  576. if ( sndName[0] != 0 ) {
  577. sndShader = declManager->FindSound( sndName );
  578. }
  579. }
  580. }
  581. if ( sndShader ) {
  582. StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL );
  583. } else {
  584. StartSound( "snd_bullethole", SND_CHANNEL_ANY, 0, false, NULL );
  585. }
  586. }
  587. a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
  588. c = cos( a );
  589. s = -sin( a );
  590. axis[2] = -dir;
  591. axis[2].Normalize();
  592. axis[2].NormalVectors( axistemp[0], axistemp[1] );
  593. axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
  594. axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
  595. textureAxis[0] = axis[0] * ( 1.0f / decalSize );
  596. textureAxis[0][3] = -( point * textureAxis[0].Normal() ) + 0.5f;
  597. textureAxis[1] = axis[1] * ( 1.0f / decalSize );
  598. textureAxis[1][3] = -( point * textureAxis[1].Normal() ) + 0.5f;
  599. for ( i = 0; i < shards.Num(); i++ ) {
  600. idFixedWinding &winding = shards[i]->winding;
  601. origin = shards[i]->clipModel->GetOrigin();
  602. axis = shards[i]->clipModel->GetAxis();
  603. float d0, d1;
  604. clipBits = -1;
  605. for ( j = 0; j < winding.GetNumPoints(); j++ ) {
  606. idVec3 p = origin + winding[j].ToVec3() * axis;
  607. st[j].x = d0 = textureAxis[0].Distance( p );
  608. st[j].y = d1 = textureAxis[1].Distance( p );
  609. bits = IEEE_FLT_SIGNBITSET( d0 );
  610. d0 = 1.0f - d0;
  611. bits |= IEEE_FLT_SIGNBITSET( d1 ) << 2;
  612. d1 = 1.0f - d1;
  613. bits |= IEEE_FLT_SIGNBITSET( d0 ) << 1;
  614. bits |= IEEE_FLT_SIGNBITSET( d1 ) << 3;
  615. clipBits &= bits;
  616. }
  617. if ( clipBits ) {
  618. continue;
  619. }
  620. idFixedWinding *decal = new (TAG_PARTICLE) idFixedWinding;
  621. shards[i]->decals.Append( decal );
  622. decal->SetNumPoints( winding.GetNumPoints() );
  623. for ( j = 0; j < winding.GetNumPoints(); j++ ) {
  624. (*decal)[j].ToVec3() = winding[j].ToVec3();
  625. (*decal)[j].s = st[j].x;
  626. (*decal)[j].t = st[j].y;
  627. }
  628. }
  629. BecomeActive( TH_UPDATEVISUALS );
  630. }
  631. /*
  632. ================
  633. idBrittleFracture::DropShard
  634. ================
  635. */
  636. void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const idVec3 &dir, const float impulse, const int time ) {
  637. int i, j, clipModelId;
  638. float dist, f;
  639. idVec3 dir2, origin;
  640. idMat3 axis;
  641. shard_t *neighbour;
  642. // don't display decals on dropped shards
  643. shard->decals.DeleteContents( true );
  644. // remove neighbour pointers of neighbours pointing to this shard
  645. for ( i = 0; i < shard->neighbours.Num(); i++ ) {
  646. neighbour = shard->neighbours[i];
  647. for ( j = 0; j < neighbour->neighbours.Num(); j++ ) {
  648. if ( neighbour->neighbours[j] == shard ) {
  649. neighbour->neighbours.RemoveIndex( j );
  650. break;
  651. }
  652. }
  653. }
  654. // remove neighbour pointers
  655. shard->neighbours.Clear();
  656. // remove the clip model from the static physics object
  657. clipModelId = shard->clipModel->GetId();
  658. physicsObj.SetClipModel( NULL, 1.0f, clipModelId, false );
  659. origin = shard->clipModel->GetOrigin();
  660. axis = shard->clipModel->GetAxis();
  661. // set the dropped time for fading
  662. shard->droppedTime = time;
  663. dir2 = origin - point;
  664. dist = dir2.Normalize();
  665. f = dist > maxShatterRadius ? 1.0f : idMath::Sqrt( idMath::Fabs( dist - minShatterRadius ) ) * ( 1.0f / idMath::Sqrt( idMath::Fabs( maxShatterRadius - minShatterRadius ) ) );
  666. // setup the physics
  667. shard->physicsObj.SetSelf( this );
  668. shard->physicsObj.SetClipModel( shard->clipModel, density );
  669. shard->physicsObj.SetMass( shardMass );
  670. shard->physicsObj.SetOrigin( origin );
  671. shard->physicsObj.SetAxis( axis );
  672. shard->physicsObj.SetBouncyness( bouncyness );
  673. shard->physicsObj.SetFriction( 0.6f, 0.6f, friction );
  674. shard->physicsObj.SetGravity( gameLocal.GetGravity() );
  675. shard->physicsObj.SetContents( CONTENTS_RENDERMODEL );
  676. shard->physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
  677. shard->physicsObj.ApplyImpulse( 0, origin, impulse * linearVelocityScale * dir );
  678. shard->physicsObj.SetAngularVelocity( dir.Cross( dir2 ) * ( f * angularVelocityScale ) );
  679. shard->clipModel->SetId( clipModelId );
  680. BecomeActive( TH_PHYSICS );
  681. }
  682. /*
  683. ================
  684. idBrittleFracture::Shatter
  685. ================
  686. */
  687. void idBrittleFracture::Shatter( const idVec3 &point, const idVec3 &impulse, const int time ) {
  688. int i;
  689. idVec3 dir;
  690. shard_t *shard;
  691. float m;
  692. if ( common->IsServer() ) {
  693. idBitMsg msg;
  694. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  695. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  696. msg.BeginWriting();
  697. msg.WriteFloat( point[0] );
  698. msg.WriteFloat( point[1] );
  699. msg.WriteFloat( point[2] );
  700. msg.WriteFloat( impulse[0] );
  701. msg.WriteFloat( impulse[1] );
  702. msg.WriteFloat( impulse[2] );
  703. ServerSendEvent( EVENT_SHATTER, &msg, true );
  704. }
  705. // Store off the event so we can rebuilt the object if we reload a savegame
  706. fractureEvent_s fractureEvent;
  707. fractureEvent.eventType = EVENT_SHATTER;
  708. fractureEvent.point = point;
  709. fractureEvent.vector = impulse;
  710. storedEvents.Append( fractureEvent );
  711. if ( time > ( gameLocal.time - SHARD_ALIVE_TIME ) ) {
  712. StartSound( "snd_shatter", SND_CHANNEL_ANY, 0, false, NULL );
  713. }
  714. if ( !IsBroken() ) {
  715. Break();
  716. }
  717. if ( fxFracture.Length() ) {
  718. idEntityFx::StartFx( fxFracture, &point, &GetPhysics()->GetAxis(), this, true );
  719. }
  720. dir = impulse;
  721. m = dir.Normalize();
  722. for ( i = 0; i < shards.Num(); i++ ) {
  723. shard = shards[i];
  724. if ( shard->droppedTime != -1 ) {
  725. continue;
  726. }
  727. if ( ( shard->clipModel->GetOrigin() - point ).LengthSqr() > Square( maxShatterRadius ) ) {
  728. continue;
  729. }
  730. DropShard( shard, point, dir, m, time );
  731. }
  732. DropFloatingIslands( point, impulse, time );
  733. }
  734. /*
  735. ================
  736. idBrittleFracture::DropFloatingIslands
  737. ================
  738. */
  739. void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 &impulse, const int time ) {
  740. int i, j, numIslands;
  741. int queueStart, queueEnd;
  742. shard_t *curShard, *nextShard, **queue;
  743. bool touchesEdge;
  744. idVec3 dir;
  745. dir = impulse;
  746. dir.Normalize();
  747. numIslands = 0;
  748. queue = (shard_t **) _alloca16( shards.Num() * sizeof(shard_t **) );
  749. for ( i = 0; i < shards.Num(); i++ ) {
  750. shards[i]->islandNum = 0;
  751. }
  752. for ( i = 0; i < shards.Num(); i++ ) {
  753. if ( shards[i]->droppedTime != -1 ) {
  754. continue;
  755. }
  756. if ( shards[i]->islandNum ) {
  757. continue;
  758. }
  759. queueStart = 0;
  760. queueEnd = 1;
  761. queue[0] = shards[i];
  762. shards[i]->islandNum = numIslands+1;
  763. touchesEdge = false;
  764. if ( shards[i]->atEdge ) {
  765. touchesEdge = true;
  766. }
  767. for ( curShard = queue[queueStart]; queueStart < queueEnd; curShard = queue[++queueStart] ) {
  768. for ( j = 0; j < curShard->neighbours.Num(); j++ ) {
  769. nextShard = curShard->neighbours[j];
  770. if ( nextShard->droppedTime != -1 ) {
  771. continue;
  772. }
  773. if ( nextShard->islandNum ) {
  774. continue;
  775. }
  776. queue[queueEnd++] = nextShard;
  777. nextShard->islandNum = numIslands+1;
  778. if ( nextShard->atEdge ) {
  779. touchesEdge = true;
  780. }
  781. }
  782. }
  783. numIslands++;
  784. // if the island is not connected to the world at any edges
  785. if ( !touchesEdge ) {
  786. for ( j = 0; j < queueEnd; j++ ) {
  787. DropShard( queue[j], point, dir, 0.0f, time );
  788. }
  789. }
  790. }
  791. }
  792. /*
  793. ================
  794. idBrittleFracture::Break
  795. ================
  796. */
  797. void idBrittleFracture::Break() {
  798. fl.takedamage = false;
  799. physicsObj.SetContents( CONTENTS_RENDERMODEL | CONTENTS_TRIGGER );
  800. }
  801. /*
  802. ================
  803. idBrittleFracture::IsBroken
  804. ================
  805. */
  806. bool idBrittleFracture::IsBroken() const {
  807. return ( fl.takedamage == false );
  808. }
  809. /*
  810. ================
  811. idBrittleFracture::Killed
  812. ================
  813. */
  814. void idBrittleFracture::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  815. if ( !disableFracture ) {
  816. ActivateTargets( this );
  817. Break();
  818. }
  819. }
  820. /*
  821. ================
  822. idBrittleFracture::AddDamageEffect
  823. ================
  824. */
  825. void idBrittleFracture::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
  826. if ( !disableFracture ) {
  827. ProjectDecal( collision.c.point, collision.c.normal, gameLocal.time, damageDefName );
  828. }
  829. }
  830. static float fractureSplitTable[] = { 1365.123f, 5.324f, 1125.34f, 50.34f, 555.252f, 100.12f, 230.53f, 10000.87f, 10000.87f };
  831. /*
  832. ================
  833. idBrittleFracture::Fracture_r
  834. ================
  835. */
  836. void idBrittleFracture::Fracture_r( idFixedWinding &w, idRandom2 & random ) {
  837. int i, j, bestPlane;
  838. float a, c, s, dist, bestDist;
  839. idVec3 origin;
  840. idPlane windingPlane, splitPlanes[2];
  841. idMat3 axis, axistemp;
  842. idFixedWinding back;
  843. idTraceModel trm;
  844. idClipModel *clipModel;
  845. while( 1 ) {
  846. origin = w.GetCenter();
  847. w.GetPlane( windingPlane );
  848. if ( w.GetArea() < maxShardArea ) {
  849. break;
  850. }
  851. // randomly create a split plane
  852. axis[2] = windingPlane.Normal();
  853. if ( isXraySurface ) {
  854. a = idMath::TWO_PI / 2.f;
  855. }
  856. else {
  857. a = random.RandomFloat() * idMath::TWO_PI;
  858. }
  859. c = cos( a );
  860. s = -sin( a );
  861. axis[2].NormalVectors( axistemp[0], axistemp[1] );
  862. axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
  863. axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
  864. // get the best split plane
  865. bestDist = 0.0f;
  866. bestPlane = 0;
  867. for ( i = 0; i < 2; i++ ) {
  868. splitPlanes[i].SetNormal( axis[i] );
  869. splitPlanes[i].FitThroughPoint( origin );
  870. for ( j = 0; j < w.GetNumPoints(); j++ ) {
  871. dist = splitPlanes[i].Distance( w[j].ToVec3() );
  872. if ( dist > bestDist ) {
  873. bestDist = dist;
  874. bestPlane = i;
  875. }
  876. }
  877. }
  878. // split the winding
  879. if ( !w.Split( &back, splitPlanes[bestPlane] ) ) {
  880. break;
  881. }
  882. // recursively create shards for the back winding
  883. Fracture_r( back, random );
  884. }
  885. // translate the winding to it's center
  886. origin = w.GetCenter();
  887. for ( j = 0; j < w.GetNumPoints(); j++ ) {
  888. w[j].ToVec3() -= origin;
  889. }
  890. w.RemoveEqualPoints();
  891. trm.SetupPolygon( w );
  892. trm.Shrink( CM_CLIP_EPSILON );
  893. clipModel = new (TAG_PHYSICS) idClipModel( trm, false );
  894. physicsObj.SetClipModel( clipModel, 1.0f, shards.Num() );
  895. physicsObj.SetOrigin( GetPhysics()->GetOrigin() + origin, shards.Num() );
  896. physicsObj.SetAxis( GetPhysics()->GetAxis(), shards.Num() );
  897. AddShard( clipModel, w );
  898. }
  899. /*
  900. ================
  901. CompareVec5
  902. ================
  903. */
  904. bool CompareVec5( const idVec5 & v0, const idVec5 & v1 ) {
  905. float dx = v0.x - v1.x;
  906. float dy = v0.y - v1.y;
  907. float dz = v0.z - v1.z;
  908. float ds = v0.s - v1.s;
  909. float dt = v0.t - v1.t;
  910. float d = ( dx * dx ) + ( dy * dy ) + ( dz * dz ) + ( ds * ds ) + ( dt + dt );
  911. return ( d == 0.0f );
  912. }
  913. /*
  914. ================
  915. idBrittleFracture::CreateFractures
  916. ================
  917. */
  918. void idBrittleFracture::CreateFractures( const idRenderModel *renderModel ) {
  919. if ( !renderModel || renderModel->NumSurfaces() < 1 ) {
  920. return;
  921. }
  922. physicsObj.SetSelf( this );
  923. physicsObj.SetOrigin( GetPhysics()->GetOrigin(), 0 );
  924. physicsObj.SetAxis( GetPhysics()->GetAxis(), 0 );
  925. const modelSurface_t * surf = renderModel->Surface( 0 );
  926. material = surf->shader;
  927. idMat3 physAxis;
  928. physAxis = physicsObj.GetAxis();
  929. if ( isXraySurface ) {
  930. idFixedWinding w;
  931. for ( int i = 0; i < 4; i++ ) {
  932. const idDrawVert * v = &surf->geometry->verts[i];
  933. w.AddPoint( idVec5( v->xyz, v->GetTexCoord() ) );
  934. }
  935. idRandom2 random( entityNumber );
  936. Fracture_r( w , random );
  937. } else
  938. {
  939. const idDrawVert * verts = surf->geometry->verts;
  940. triIndex_t * indexes = surf->geometry->indexes;
  941. for ( int j = 0; j < surf->geometry->numIndexes; j += 3 ) {
  942. int i0 = indexes[ j + 0 ];
  943. int i1 = indexes[ j + 1 ];
  944. int i2 = indexes[ j + 2 ];
  945. idFixedWinding w;
  946. w.AddPoint( idVec5( verts[i2].xyz, verts[i2].GetTexCoord() ) );
  947. w.AddPoint( idVec5( verts[i1].xyz, verts[i1].GetTexCoord() ) );
  948. w.AddPoint( idVec5( verts[i0].xyz, verts[i0].GetTexCoord() ) );
  949. idPlane p1;
  950. w.GetPlane( p1 );
  951. for ( int k = j + 3; k < surf->geometry->numIndexes && ( w.GetNumPoints() + 1 < MAX_POINTS_ON_WINDING ); k += 3 ) {
  952. int i3 = indexes[ k + 0 ];
  953. int i4 = indexes[ k + 1 ];
  954. int i5 = indexes[ k + 2 ];
  955. idFixedWinding w2;
  956. w2.AddPoint( idVec5( verts[i5].xyz, verts[i5].GetTexCoord() ) );
  957. w2.AddPoint( idVec5( verts[i4].xyz, verts[i4].GetTexCoord() ) );
  958. w2.AddPoint( idVec5( verts[i3].xyz, verts[i3].GetTexCoord() ) );
  959. idPlane p2;
  960. w2.GetPlane( p2 );
  961. if ( p1 != p2 ) {
  962. break;
  963. }
  964. bool found = false;
  965. for ( int w1i = 0; w1i < w.GetNumPoints(); w1i++ ) {
  966. for ( int w2i = 0; w2i < w2.GetNumPoints(); w2i++ ) {
  967. if ( CompareVec5( w[w1i], w2[w2i] ) && CompareVec5( w[(w1i+1)%w.GetNumPoints()], w2[(w2i+2)%w2.GetNumPoints()] ) ) {
  968. w.InsertPoint( w2[(w2i+1)%w2.GetNumPoints()], (w1i+1)%w.GetNumPoints() );
  969. j = k;
  970. found = true;
  971. break;
  972. }
  973. }
  974. if ( found ) {
  975. break;
  976. }
  977. }
  978. if ( !found ) {
  979. break;
  980. }
  981. }
  982. idRandom2 random( entityNumber );
  983. Fracture_r( w, random );
  984. }
  985. }
  986. physicsObj.SetContents( material->GetContentFlags() );
  987. SetPhysics( &physicsObj );
  988. }
  989. /*
  990. ================
  991. idBrittleFracture::FindNeighbours
  992. ================
  993. */
  994. void idBrittleFracture::FindNeighbours() {
  995. int i, j, k, l;
  996. idVec3 p1, p2, dir;
  997. idMat3 axis;
  998. idPlane plane[4];
  999. for ( i = 0; i < shards.Num(); i++ ) {
  1000. shard_t *shard1 = shards[i];
  1001. const idWinding &w1 = shard1->winding;
  1002. const idVec3 &origin1 = shard1->clipModel->GetOrigin();
  1003. const idMat3 &axis1 = shard1->clipModel->GetAxis();
  1004. for ( k = 0; k < w1.GetNumPoints(); k++ ) {
  1005. p1 = origin1 + w1[k].ToVec3() * axis1;
  1006. p2 = origin1 + w1[(k+1)%w1.GetNumPoints()].ToVec3() * axis1;
  1007. dir = p2 - p1;
  1008. dir.Normalize();
  1009. axis = dir.ToMat3();
  1010. plane[0].SetNormal( dir );
  1011. plane[0].FitThroughPoint( p1 );
  1012. plane[1].SetNormal( -dir );
  1013. plane[1].FitThroughPoint( p2 );
  1014. plane[2].SetNormal( axis[1] );
  1015. plane[2].FitThroughPoint( p1 );
  1016. plane[3].SetNormal( axis[2] );
  1017. plane[3].FitThroughPoint( p1 );
  1018. for ( j = 0; j < shards.Num(); j++ ) {
  1019. if ( i == j ) {
  1020. continue;
  1021. }
  1022. shard_t *shard2 = shards[j];
  1023. for ( l = 0; l < shard1->neighbours.Num(); l++ ) {
  1024. if ( shard1->neighbours[l] == shard2 ) {
  1025. break;
  1026. }
  1027. }
  1028. if ( l < shard1->neighbours.Num() ) {
  1029. continue;
  1030. }
  1031. const idWinding &w2 = shard2->winding;
  1032. const idVec3 &origin2 = shard2->clipModel->GetOrigin();
  1033. const idMat3 &axis2 = shard2->clipModel->GetAxis();
  1034. for ( l = w2.GetNumPoints()-1; l >= 0; l-- ) {
  1035. p1 = origin2 + w2[l].ToVec3() * axis2;
  1036. p2 = origin2 + w2[(l-1+w2.GetNumPoints())%w2.GetNumPoints()].ToVec3() * axis2;
  1037. if ( plane[0].Side( p2, 0.1f ) == SIDE_FRONT && plane[1].Side( p1, 0.1f ) == SIDE_FRONT ) {
  1038. if ( plane[2].Side( p1, 0.1f ) == SIDE_ON && plane[3].Side( p1, 0.1f ) == SIDE_ON ) {
  1039. if ( plane[2].Side( p2, 0.1f ) == SIDE_ON && plane[3].Side( p2, 0.1f ) == SIDE_ON ) {
  1040. shard1->neighbours.Append( shard2 );
  1041. shard1->edgeHasNeighbour[k] = true;
  1042. shard2->neighbours.Append( shard1 );
  1043. shard2->edgeHasNeighbour[(l-1+w2.GetNumPoints())%w2.GetNumPoints()] = true;
  1044. break;
  1045. }
  1046. }
  1047. }
  1048. }
  1049. }
  1050. }
  1051. for ( k = 0; k < w1.GetNumPoints(); k++ ) {
  1052. if ( !shard1->edgeHasNeighbour[k] ) {
  1053. break;
  1054. }
  1055. }
  1056. if ( k < w1.GetNumPoints() ) {
  1057. shard1->atEdge = true;
  1058. } else {
  1059. shard1->atEdge = false;
  1060. }
  1061. }
  1062. }
  1063. /*
  1064. ================
  1065. idBrittleFracture::Event_Activate
  1066. ================
  1067. */
  1068. void idBrittleFracture::Event_Activate( idEntity *activator ) {
  1069. disableFracture = false;
  1070. if ( health <= 0 ) {
  1071. Break();
  1072. }
  1073. }
  1074. /*
  1075. ================
  1076. idBrittleFracture::Event_Touch
  1077. ================
  1078. */
  1079. void idBrittleFracture::Event_Touch( idEntity *other, trace_t *trace ) {
  1080. idVec3 point, impulse;
  1081. // Let the server handle this, clients dont' predict it
  1082. if ( common->IsClient() ) {
  1083. return;
  1084. }
  1085. if ( !IsBroken() ) {
  1086. return;
  1087. }
  1088. if ( trace->c.id < 0 || trace->c.id >= shards.Num() ) {
  1089. return;
  1090. }
  1091. point = shards[trace->c.id]->clipModel->GetOrigin();
  1092. impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass();
  1093. Shatter( point, impulse, gameLocal.time );
  1094. }
  1095. /*
  1096. ================
  1097. idBrittleFracture::ClientThink
  1098. ================
  1099. */
  1100. void idBrittleFracture::ClientThink( const int curTime, const float fraction, const bool predict ) {
  1101. // only think forward because the state is not synced through snapshots
  1102. if ( !gameLocal.isNewFrame ) {
  1103. return;
  1104. }
  1105. Think();
  1106. }
  1107. /*
  1108. ================
  1109. idBrittleFracture::ClientPredictionThink
  1110. ================
  1111. */
  1112. void idBrittleFracture::ClientPredictionThink() {
  1113. // only think forward because the state is not synced through snapshots
  1114. if ( !gameLocal.isNewFrame ) {
  1115. return;
  1116. }
  1117. Think();
  1118. }
  1119. /*
  1120. ================
  1121. idBrittleFracture::ClientReceiveEvent
  1122. ================
  1123. */
  1124. bool idBrittleFracture::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  1125. idVec3 point, dir;
  1126. switch( event ) {
  1127. case EVENT_PROJECT_DECAL: {
  1128. point[0] = msg.ReadFloat();
  1129. point[1] = msg.ReadFloat();
  1130. point[2] = msg.ReadFloat();
  1131. dir[0] = msg.ReadFloat();
  1132. dir[1] = msg.ReadFloat();
  1133. dir[2] = msg.ReadFloat();
  1134. ProjectDecal( point, dir, time, NULL );
  1135. return true;
  1136. }
  1137. case EVENT_SHATTER: {
  1138. point[0] = msg.ReadFloat();
  1139. point[1] = msg.ReadFloat();
  1140. point[2] = msg.ReadFloat();
  1141. dir[0] = msg.ReadFloat();
  1142. dir[1] = msg.ReadFloat();
  1143. dir[2] = msg.ReadFloat();
  1144. Shatter( point, dir, time );
  1145. return true;
  1146. }
  1147. default: {
  1148. return idEntity::ClientReceiveEvent( event, time, msg );
  1149. }
  1150. }
  1151. }