]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/BrittleFracture.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / d3xp / BrittleFracture.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34
35 CLASS_DECLARATION( idEntity, idBrittleFracture )
36         EVENT( EV_Activate, idBrittleFracture::Event_Activate )
37         EVENT( EV_Touch, idBrittleFracture::Event_Touch )
38 END_CLASS
39
40 const int SHARD_ALIVE_TIME      = 5000;
41 const int SHARD_FADE_START      = 2000;
42
43 static const char *brittleFracture_SnapshotName = "_BrittleFracture_Snapshot_";
44
45 /*
46 ================
47 idBrittleFracture::idBrittleFracture
48 ================
49 */
50 idBrittleFracture::idBrittleFracture( void ) {
51         material = NULL;
52         decalMaterial = NULL;
53         decalSize = 0.0f;
54         maxShardArea = 0.0f;
55         maxShatterRadius = 0.0f;
56         minShatterRadius = 0.0f;
57         linearVelocityScale = 0.0f;
58         angularVelocityScale = 0.0f;
59         shardMass = 0.0f;
60         density = 0.0f;
61         friction = 0.0f;
62         bouncyness = 0.0f;
63         fxFracture.Clear();
64
65         bounds.Clear();
66         disableFracture = false;
67
68         lastRenderEntityUpdate = -1;
69         changed = false;
70
71         fl.networkSync = true;
72
73 #ifdef _D3XP
74         isXraySurface = false;
75 #endif
76 }
77
78 /*
79 ================
80 idBrittleFracture::~idBrittleFracture
81 ================
82 */
83 idBrittleFracture::~idBrittleFracture( void ) {
84         int i;
85
86         for ( i = 0; i < shards.Num(); i++ ) {
87                 shards[i]->decals.DeleteContents( true );
88                 delete shards[i];
89         }
90
91         // make sure the render entity is freed before the model is freed
92         FreeModelDef();
93         renderModelManager->FreeModel( renderEntity.hModel );
94 }
95
96 /*
97 ================
98 idBrittleFracture::Save
99 ================
100 */
101 void idBrittleFracture::Save( idSaveGame *savefile ) const {
102         int i, j;
103
104         savefile->WriteInt( health );
105         entityFlags_s flags = fl;
106         LittleBitField( &flags, sizeof( flags ) );
107         savefile->Write( &flags, sizeof( flags ) );
108         
109         // setttings
110         savefile->WriteMaterial( material );
111         savefile->WriteMaterial( decalMaterial );
112         savefile->WriteFloat( decalSize );
113         savefile->WriteFloat( maxShardArea );
114         savefile->WriteFloat( maxShatterRadius );
115         savefile->WriteFloat( minShatterRadius );
116         savefile->WriteFloat( linearVelocityScale );
117         savefile->WriteFloat( angularVelocityScale );
118         savefile->WriteFloat( shardMass );
119         savefile->WriteFloat( density );
120         savefile->WriteFloat( friction );
121         savefile->WriteFloat( bouncyness );
122         savefile->WriteString( fxFracture );
123
124         // state
125         savefile->WriteBounds( bounds );
126         savefile->WriteBool( disableFracture );
127
128         savefile->WriteInt( lastRenderEntityUpdate );
129         savefile->WriteBool( changed );
130
131         savefile->WriteStaticObject( physicsObj );
132
133         savefile->WriteInt( shards.Num() );
134         for ( i = 0; i < shards.Num(); i++ ) {
135                 savefile->WriteWinding( shards[i]->winding );
136
137                 savefile->WriteInt( shards[i]->decals.Num() );
138                 for ( j = 0; j < shards[i]->decals.Num(); j++ ) {
139                         savefile->WriteWinding( *shards[i]->decals[j] );
140                 }
141
142                 savefile->WriteInt( shards[i]->neighbours.Num() );
143                 for ( j = 0; j < shards[i]->neighbours.Num(); j++ ) {
144                         int index = shards.FindIndex(shards[i]->neighbours[j]);
145                         assert(index != -1);
146                         savefile->WriteInt( index );
147                 }
148
149                 savefile->WriteInt( shards[i]->edgeHasNeighbour.Num() );
150                 for ( j = 0; j < shards[i]->edgeHasNeighbour.Num(); j++ ) {
151                         savefile->WriteBool( shards[i]->edgeHasNeighbour[j] );
152                 }
153
154                 savefile->WriteInt( shards[i]->droppedTime );
155                 savefile->WriteInt( shards[i]->islandNum );
156                 savefile->WriteBool( shards[i]->atEdge );
157                 savefile->WriteStaticObject( shards[i]->physicsObj );
158         }
159
160 #ifdef _D3XP
161         savefile->WriteBool( isXraySurface );
162 #endif
163 }
164
165 /*
166 ================
167 idBrittleFracture::Restore
168 ================
169 */
170 void idBrittleFracture::Restore( idRestoreGame *savefile ) {
171         int i, j , num;
172
173         renderEntity.hModel = renderModelManager->AllocModel();
174         renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
175         renderEntity.callback = idBrittleFracture::ModelCallback;
176         renderEntity.noShadow = true;
177         renderEntity.noSelfShadow = true;
178         renderEntity.noDynamicInteractions = false;
179
180         savefile->ReadInt( health );
181         savefile->Read( &fl, sizeof( fl ) );
182         LittleBitField( &fl, sizeof( fl ) );
183
184         // setttings
185         savefile->ReadMaterial( material );
186         savefile->ReadMaterial( decalMaterial );
187         savefile->ReadFloat( decalSize );
188         savefile->ReadFloat( maxShardArea );
189         savefile->ReadFloat( maxShatterRadius );
190         savefile->ReadFloat( minShatterRadius );
191         savefile->ReadFloat( linearVelocityScale );
192         savefile->ReadFloat( angularVelocityScale );
193         savefile->ReadFloat( shardMass );
194         savefile->ReadFloat( density );
195         savefile->ReadFloat( friction );
196         savefile->ReadFloat( bouncyness );
197         savefile->ReadString( fxFracture );
198
199         // state
200         savefile->ReadBounds(bounds);
201         savefile->ReadBool( disableFracture );
202
203         savefile->ReadInt( lastRenderEntityUpdate );
204         savefile->ReadBool( changed );
205
206         savefile->ReadStaticObject( physicsObj );
207         RestorePhysics( &physicsObj );
208
209         savefile->ReadInt( num );
210         shards.SetNum( num );
211         for ( i = 0; i < num; i++ ) {
212                 shards[i] = new shard_t;
213         }
214
215         for ( i = 0; i < num; i++ ) {
216                 savefile->ReadWinding( shards[i]->winding );
217
218                 savefile->ReadInt( j );
219                 shards[i]->decals.SetNum( j );
220                 for ( j = 0; j < shards[i]->decals.Num(); j++ ) {
221                         shards[i]->decals[j] = new idFixedWinding;
222                         savefile->ReadWinding( *shards[i]->decals[j] );
223                 }
224
225                 savefile->ReadInt( j );
226                 shards[i]->neighbours.SetNum( j );
227                 for ( j = 0; j < shards[i]->neighbours.Num(); j++ ) {
228                         int index;
229                         savefile->ReadInt( index );
230                         assert(index != -1);
231                         shards[i]->neighbours[j] = shards[index];
232                 }
233
234                 savefile->ReadInt( j );
235                 shards[i]->edgeHasNeighbour.SetNum( j );
236                 for ( j = 0; j < shards[i]->edgeHasNeighbour.Num(); j++ ) {
237                         savefile->ReadBool( shards[i]->edgeHasNeighbour[j] );
238                 }
239
240                 savefile->ReadInt( shards[i]->droppedTime );
241                 savefile->ReadInt( shards[i]->islandNum );
242                 savefile->ReadBool( shards[i]->atEdge );
243                 savefile->ReadStaticObject( shards[i]->physicsObj );
244                 if ( shards[i]->droppedTime < 0 ) {
245                         shards[i]->clipModel = physicsObj.GetClipModel( i );
246                 } else {
247                         shards[i]->clipModel = shards[i]->physicsObj.GetClipModel();
248                 }
249         }
250
251 #ifdef _D3XP
252         savefile->ReadBool( isXraySurface );
253 #endif
254 }
255
256 /*
257 ================
258 idBrittleFracture::Spawn
259 ================
260 */
261 void idBrittleFracture::Spawn( void ) {
262
263         // get shard properties
264         decalMaterial = declManager->FindMaterial( spawnArgs.GetString( "mtr_decal" ) );
265         decalSize = spawnArgs.GetFloat( "decalSize", "40" );
266         maxShardArea = spawnArgs.GetFloat( "maxShardArea", "200" );
267         maxShardArea = idMath::ClampFloat( 100, 10000, maxShardArea );
268         maxShatterRadius = spawnArgs.GetFloat( "maxShatterRadius", "40" );
269         minShatterRadius = spawnArgs.GetFloat( "minShatterRadius", "10" );
270         linearVelocityScale = spawnArgs.GetFloat( "linearVelocityScale", "0.1" );
271         angularVelocityScale = spawnArgs.GetFloat( "angularVelocityScale", "40" );
272         fxFracture = spawnArgs.GetString( "fx" );
273
274         // get rigid body properties
275         shardMass = spawnArgs.GetFloat( "shardMass", "20" );
276         shardMass = idMath::ClampFloat( 0.001f, 1000.0f, shardMass );
277         spawnArgs.GetFloat( "density", "0.1", density );
278         density = idMath::ClampFloat( 0.001f, 1000.0f, density );
279         spawnArgs.GetFloat( "friction", "0.4", friction );
280         friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
281         spawnArgs.GetFloat( "bouncyness", "0.01", bouncyness );
282         bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
283
284         disableFracture = spawnArgs.GetBool( "disableFracture", "0" );
285         health = spawnArgs.GetInt( "health", "40" );
286         fl.takedamage = true;
287
288         // FIXME: set "bleed" so idProjectile calls AddDamageEffect
289         spawnArgs.SetBool( "bleed", 1 );
290
291 #ifdef _D3XP
292         // check for xray surface
293         if ( 1 ) {
294                 const idRenderModel *model = renderEntity.hModel;
295
296                 isXraySurface = false;
297
298                 for ( int i = 0; i < model->NumSurfaces(); i++ ) {
299                         const modelSurface_t *surf = model->Surface( i );
300
301                         if ( idStr( surf->shader->GetName() ) == "textures/smf/window_scratch" ) {
302                                 isXraySurface = true;
303                                 break;
304                         }
305                 }
306         }
307 #endif
308
309         CreateFractures( renderEntity.hModel );
310
311         FindNeighbours();
312
313         renderEntity.hModel = renderModelManager->AllocModel();
314         renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
315         renderEntity.callback = idBrittleFracture::ModelCallback;
316         renderEntity.noShadow = true;
317         renderEntity.noSelfShadow = true;
318         renderEntity.noDynamicInteractions = false;
319 }
320
321 /*
322 ================
323 idBrittleFracture::AddShard
324 ================
325 */
326 void idBrittleFracture::AddShard( idClipModel *clipModel, idFixedWinding &w ) {
327         shard_t *shard = new shard_t;
328         shard->clipModel = clipModel;
329         shard->droppedTime = -1;
330         shard->winding = w;
331         shard->decals.Clear();
332         shard->edgeHasNeighbour.AssureSize( w.GetNumPoints(), false );
333         shard->neighbours.Clear();
334         shard->atEdge = false;
335         shards.Append( shard );
336 }
337
338 /*
339 ================
340 idBrittleFracture::RemoveShard
341 ================
342 */
343 void idBrittleFracture::RemoveShard( int index ) {
344         int i;
345
346         delete shards[index];
347         shards.RemoveIndex( index );
348         physicsObj.RemoveIndex( index );
349
350         for ( i = index; i < shards.Num(); i++ ) {
351                 shards[i]->clipModel->SetId( i );
352         }
353 }
354
355 /*
356 ================
357 idBrittleFracture::UpdateRenderEntity
358 ================
359 */
360 bool idBrittleFracture::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
361         int i, j, k, n, msec, numTris, numDecalTris;
362         float fade;
363         dword packedColor;
364         srfTriangles_t *tris, *decalTris;
365         modelSurface_t surface;
366         idDrawVert *v;
367         idPlane plane;
368         idMat3 tangents;
369
370         // this may be triggered by a model trace or other non-view related source,
371         // to which we should look like an empty model
372         if ( !renderView ) {
373                 return false;
374         }
375
376         // don't regenerate it if it is current
377         if ( lastRenderEntityUpdate == gameLocal.time || !changed ) {
378                 return false;
379         }
380
381         lastRenderEntityUpdate = gameLocal.time;
382         changed = false;
383
384         numTris = 0;
385         numDecalTris = 0;
386         for ( i = 0; i < shards.Num(); i++ ) {
387                 n = shards[i]->winding.GetNumPoints();
388                 if ( n > 2 ) {
389                         numTris += n - 2;
390                 }
391                 for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
392                         n = shards[i]->decals[k]->GetNumPoints();
393                         if ( n > 2 ) {
394                                 numDecalTris += n - 2;
395                         }
396                 }
397         }
398
399         // FIXME: re-use model surfaces
400         renderEntity->hModel->InitEmpty( brittleFracture_SnapshotName );
401
402         // allocate triangle surfaces for the fractures and decals
403         tris = renderEntity->hModel->AllocSurfaceTriangles( numTris * 3, material->ShouldCreateBackSides() ? numTris * 6 : numTris * 3 );
404         decalTris = renderEntity->hModel->AllocSurfaceTriangles( numDecalTris * 3, decalMaterial->ShouldCreateBackSides() ? numDecalTris * 6 : numDecalTris * 3 );
405
406         for ( i = 0; i < shards.Num(); i++ ) {
407                 const idVec3 &origin = shards[i]->clipModel->GetOrigin();
408                 const idMat3 &axis = shards[i]->clipModel->GetAxis();
409
410                 fade = 1.0f;
411                 if ( shards[i]->droppedTime >= 0 ) {
412                         msec = gameLocal.time - shards[i]->droppedTime - SHARD_FADE_START;
413                         if ( msec > 0 ) {
414                                 fade = 1.0f - (float) msec / ( SHARD_ALIVE_TIME - SHARD_FADE_START );
415                         }
416                 }
417                 packedColor = PackColor( idVec4( renderEntity->shaderParms[ SHADERPARM_RED ] * fade,
418                                                                                 renderEntity->shaderParms[ SHADERPARM_GREEN ] * fade,
419                                         renderEntity->shaderParms[ SHADERPARM_BLUE ] * fade,
420                                                                                 fade ) );
421
422                 const idWinding &winding = shards[i]->winding;
423
424                 winding.GetPlane( plane );
425                 tangents = ( plane.Normal() * axis ).ToMat3();
426
427                 for ( j = 2; j < winding.GetNumPoints(); j++ ) {
428
429                         v = &tris->verts[tris->numVerts++];
430                         v->Clear();
431                         v->xyz = origin + winding[0].ToVec3() * axis;
432                         v->st[0] = winding[0].s;
433                         v->st[1] = winding[0].t;
434                         v->normal = tangents[0];
435                         v->tangents[0] = tangents[1];
436                         v->tangents[1] = tangents[2];
437                         v->SetColor( packedColor );
438
439                         v = &tris->verts[tris->numVerts++];
440                         v->Clear();
441                         v->xyz = origin + winding[j-1].ToVec3() * axis;
442                         v->st[0] = winding[j-1].s;
443                         v->st[1] = winding[j-1].t;
444                         v->normal = tangents[0];
445                         v->tangents[0] = tangents[1];
446                         v->tangents[1] = tangents[2];
447                         v->SetColor( packedColor );
448
449                         v = &tris->verts[tris->numVerts++];
450                         v->Clear();
451                         v->xyz = origin + winding[j].ToVec3() * axis;
452                         v->st[0] = winding[j].s;
453                         v->st[1] = winding[j].t;
454                         v->normal = tangents[0];
455                         v->tangents[0] = tangents[1];
456                         v->tangents[1] = tangents[2];
457                         v->SetColor( packedColor );
458
459                         tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
460                         tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
461                         tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
462
463                         if ( material->ShouldCreateBackSides() ) {
464
465                                 tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
466                                 tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
467                                 tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
468                         }
469                 }
470
471                 for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
472                         const idWinding &decalWinding = *shards[i]->decals[k];
473
474                         for ( j = 2; j < decalWinding.GetNumPoints(); j++ ) {
475
476                                 v = &decalTris->verts[decalTris->numVerts++];
477                                 v->Clear();
478                                 v->xyz = origin + decalWinding[0].ToVec3() * axis;
479                                 v->st[0] = decalWinding[0].s;
480                                 v->st[1] = decalWinding[0].t;
481                                 v->normal = tangents[0];
482                                 v->tangents[0] = tangents[1];
483                                 v->tangents[1] = tangents[2];
484                                 v->SetColor( packedColor );
485
486                                 v = &decalTris->verts[decalTris->numVerts++];
487                                 v->Clear();
488                                 v->xyz = origin + decalWinding[j-1].ToVec3() * axis;
489                                 v->st[0] = decalWinding[j-1].s;
490                                 v->st[1] = decalWinding[j-1].t;
491                                 v->normal = tangents[0];
492                                 v->tangents[0] = tangents[1];
493                                 v->tangents[1] = tangents[2];
494                                 v->SetColor( packedColor );
495
496                                 v = &decalTris->verts[decalTris->numVerts++];
497                                 v->Clear();
498                                 v->xyz = origin + decalWinding[j].ToVec3() * axis;
499                                 v->st[0] = decalWinding[j].s;
500                                 v->st[1] = decalWinding[j].t;
501                                 v->normal = tangents[0];
502                                 v->tangents[0] = tangents[1];
503                                 v->tangents[1] = tangents[2];
504                                 v->SetColor( packedColor );
505
506                                 decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
507                                 decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
508                                 decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
509
510                                 if ( decalMaterial->ShouldCreateBackSides() ) {
511
512                                         decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
513                                         decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
514                                         decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
515                                 }
516                         }
517                 }
518         }
519
520         tris->tangentsCalculated = true;
521         decalTris->tangentsCalculated = true;
522
523         SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts );
524         SIMDProcessor->MinMax( decalTris->bounds[0], decalTris->bounds[1], decalTris->verts, decalTris->numVerts );
525
526         memset( &surface, 0, sizeof( surface ) );
527         surface.shader = material;
528         surface.id = 0;
529         surface.geometry = tris;
530         renderEntity->hModel->AddSurface( surface );
531
532         memset( &surface, 0, sizeof( surface ) );
533         surface.shader = decalMaterial;
534         surface.id = 1;
535         surface.geometry = decalTris;
536         renderEntity->hModel->AddSurface( surface );
537
538         return true;
539 }
540
541 /*
542 ================
543 idBrittleFracture::ModelCallback
544 ================
545 */
546 bool idBrittleFracture::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
547         const idBrittleFracture *ent;
548
549         ent = static_cast<idBrittleFracture *>(gameLocal.entities[ renderEntity->entityNum ]);
550         if ( !ent ) {
551                 gameLocal.Error( "idBrittleFracture::ModelCallback: callback with NULL game entity" );
552         }
553
554         return ent->UpdateRenderEntity( renderEntity, renderView );
555 }
556
557 /*
558 ================
559 idBrittleFracture::Present
560 ================
561 */
562 void idBrittleFracture::Present() {
563
564         // don't present to the renderer if the entity hasn't changed
565         if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
566                 return;
567         }
568         BecomeInactive( TH_UPDATEVISUALS );
569
570         renderEntity.bounds = bounds;
571         renderEntity.origin.Zero();
572         renderEntity.axis.Identity();
573
574         // force an update because the bounds/origin/axis may stay the same while the model changes
575         renderEntity.forceUpdate = true;
576
577         // add to refresh list
578         if ( modelDefHandle == -1 ) {
579                 modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
580         } else {
581                 gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
582         }
583
584         changed = true;
585 }
586
587 /*
588 ================
589 idBrittleFracture::Think
590 ================
591 */
592 void idBrittleFracture::Think( void ) {
593         int i, startTime, endTime, droppedTime;
594         shard_t *shard;
595         bool atRest = true, fading = false;
596
597         // remove overdue shards
598         for ( i = 0; i < shards.Num(); i++ ) {
599                 droppedTime = shards[i]->droppedTime;
600                 if ( droppedTime != -1 ) {
601                         if ( gameLocal.time - droppedTime > SHARD_ALIVE_TIME ) {
602                                 RemoveShard( i );
603                                 i--;
604                         }
605                         fading = true;
606                 }
607         }
608
609         // remove the entity when nothing is visible
610         if ( !shards.Num() ) {
611                 PostEventMS( &EV_Remove, 0 );
612                 return;
613         }
614
615         if ( thinkFlags & TH_PHYSICS ) {
616
617                 startTime = gameLocal.previousTime;
618                 endTime = gameLocal.time;
619
620                 // run physics on shards
621                 for ( i = 0; i < shards.Num(); i++ ) {
622                         shard = shards[i];
623
624                         if ( shard->droppedTime == -1 ) {
625                                 continue;
626                         }
627
628                         shard->physicsObj.Evaluate( endTime - startTime, endTime );
629
630                         if ( !shard->physicsObj.IsAtRest() ) {
631                                 atRest = false;
632                         }
633                 }
634
635                 if ( atRest ) {
636                         BecomeInactive( TH_PHYSICS );
637                 } else {
638                         BecomeActive( TH_PHYSICS );
639                 }
640         }
641
642         if ( !atRest || bounds.IsCleared() ) {
643                 bounds.Clear();
644                 for ( i = 0; i < shards.Num(); i++ ) {
645                         bounds.AddBounds( shards[i]->clipModel->GetAbsBounds() );
646                 }
647         }
648
649         if ( fading ) {
650                 BecomeActive( TH_UPDATEVISUALS | TH_THINK );
651         } else {
652                 BecomeInactive( TH_THINK );
653         }
654
655         RunPhysics();
656         Present();
657 }
658
659 /*
660 ================
661 idBrittleFracture::ApplyImpulse
662 ================
663 */
664 void idBrittleFracture::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
665
666         if ( id < 0 || id >= shards.Num() ) {
667                 return;
668         }
669
670         if ( shards[id]->droppedTime != -1 ) {
671                 shards[id]->physicsObj.ApplyImpulse( 0, point, impulse );
672         } else if ( health <= 0 && !disableFracture ) {
673                 Shatter( point, impulse, gameLocal.time );
674         }
675 }
676
677 /*
678 ================
679 idBrittleFracture::AddForce
680 ================
681 */
682 void idBrittleFracture::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
683
684         if ( id < 0 || id >= shards.Num() ) {
685                 return;
686         }
687
688         if ( shards[id]->droppedTime != -1 ) {
689                 shards[id]->physicsObj.AddForce( 0, point, force );
690         } else if ( health <= 0 && !disableFracture ) {
691                 Shatter( point, force, gameLocal.time );
692         }
693 }
694
695 /*
696 ================
697 idBrittleFracture::ProjectDecal
698 ================
699 */
700 void idBrittleFracture::ProjectDecal( const idVec3 &point, const idVec3 &dir, const int time, const char *damageDefName ) {
701         int i, j, bits, clipBits;
702         float a, c, s;
703         idVec2 st[MAX_POINTS_ON_WINDING];
704         idVec3 origin;
705         idMat3 axis, axistemp;
706         idPlane textureAxis[2];
707
708         if ( gameLocal.isServer ) {
709                 idBitMsg        msg;
710                 byte            msgBuf[MAX_EVENT_PARAM_SIZE];
711
712                 msg.Init( msgBuf, sizeof( msgBuf ) );
713                 msg.BeginWriting();
714                 msg.WriteFloat( point[0] );
715                 msg.WriteFloat( point[1] );
716                 msg.WriteFloat( point[2] );
717                 msg.WriteFloat( dir[0] );
718                 msg.WriteFloat( dir[1] );
719                 msg.WriteFloat( dir[2] );
720                 ServerSendEvent( EVENT_PROJECT_DECAL, &msg, true, -1 );
721         }
722
723         if ( time >= gameLocal.time ) {
724                 // try to get the sound from the damage def
725                 const idDeclEntityDef *damageDef = NULL;
726                 const idSoundShader *sndShader = NULL;
727                 if ( damageDefName ) {
728                         damageDef = gameLocal.FindEntityDef( damageDefName, false );
729                         if ( damageDef ) {
730                                 sndShader = declManager->FindSound( damageDef->dict.GetString( "snd_shatter", "" ) );
731                         }
732                 }
733
734                 if ( sndShader ) {
735                         StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL );
736                 } else {
737                         StartSound( "snd_bullethole", SND_CHANNEL_ANY, 0, false, NULL );
738                 }
739         }
740
741         a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
742         c = cos( a );
743         s = -sin( a );
744
745         axis[2] = -dir;
746         axis[2].Normalize();
747         axis[2].NormalVectors( axistemp[0], axistemp[1] );
748         axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
749         axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
750
751         textureAxis[0] = axis[0] * ( 1.0f / decalSize );
752         textureAxis[0][3] = -( point * textureAxis[0].Normal() ) + 0.5f;
753
754         textureAxis[1] = axis[1] * ( 1.0f / decalSize );
755         textureAxis[1][3] = -( point * textureAxis[1].Normal() ) + 0.5f;
756
757         for ( i = 0; i < shards.Num(); i++ ) {
758                 idFixedWinding &winding = shards[i]->winding;
759                 origin = shards[i]->clipModel->GetOrigin();
760                 axis = shards[i]->clipModel->GetAxis();
761                 float d0, d1;
762
763                 clipBits = -1;
764                 for ( j = 0; j < winding.GetNumPoints(); j++ ) {
765                         idVec3 p = origin + winding[j].ToVec3() * axis;
766
767                         st[j].x = d0 = textureAxis[0].Distance( p );
768                         st[j].y = d1 = textureAxis[1].Distance( p );
769
770                         bits = FLOATSIGNBITSET( d0 );
771                         d0 = 1.0f - d0;
772                         bits |= FLOATSIGNBITSET( d1 ) << 2;
773                         d1 = 1.0f - d1;
774                         bits |= FLOATSIGNBITSET( d0 ) << 1;
775                         bits |= FLOATSIGNBITSET( d1 ) << 3;
776
777                         clipBits &= bits;
778                 }
779
780                 if ( clipBits ) {
781                         continue;
782                 }
783
784                 idFixedWinding *decal = new idFixedWinding;
785                 shards[i]->decals.Append( decal );
786
787                 decal->SetNumPoints( winding.GetNumPoints() );
788                 for ( j = 0; j < winding.GetNumPoints(); j++ ) {
789                         (*decal)[j].ToVec3() = winding[j].ToVec3();
790                         (*decal)[j].s = st[j].x;
791                         (*decal)[j].t = st[j].y;
792                 }
793         }
794
795         BecomeActive( TH_UPDATEVISUALS );
796 }
797
798 /*
799 ================
800 idBrittleFracture::DropShard
801 ================
802 */
803 void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const idVec3 &dir, const float impulse, const int time ) {
804         int i, j, clipModelId;
805         float dist, f;
806         idVec3 dir2, origin;
807         idMat3 axis;
808         shard_t *neighbour;
809
810         // don't display decals on dropped shards
811         shard->decals.DeleteContents( true );
812
813         // remove neighbour pointers of neighbours pointing to this shard
814         for ( i = 0; i < shard->neighbours.Num(); i++ ) {
815                 neighbour = shard->neighbours[i];
816                 for ( j = 0; j < neighbour->neighbours.Num(); j++ ) {
817                         if ( neighbour->neighbours[j] == shard ) {
818                                 neighbour->neighbours.RemoveIndex( j );
819                                 break;
820                         }
821                 }
822         }
823
824         // remove neighbour pointers
825         shard->neighbours.Clear();
826
827         // remove the clip model from the static physics object
828         clipModelId = shard->clipModel->GetId();
829         physicsObj.SetClipModel( NULL, 1.0f, clipModelId, false );
830
831         origin = shard->clipModel->GetOrigin();
832         axis = shard->clipModel->GetAxis();
833
834         // set the dropped time for fading
835         shard->droppedTime = time;
836
837         dir2 = origin - point;
838         dist = dir2.Normalize();
839         f = dist > maxShatterRadius ? 1.0f : idMath::Sqrt( dist - minShatterRadius ) * ( 1.0f / idMath::Sqrt( maxShatterRadius - minShatterRadius ) );
840
841         // setup the physics
842         shard->physicsObj.SetSelf( this );
843         shard->physicsObj.SetClipModel( shard->clipModel, density );
844         shard->physicsObj.SetMass( shardMass );
845         shard->physicsObj.SetOrigin( origin );
846         shard->physicsObj.SetAxis( axis );
847         shard->physicsObj.SetBouncyness( bouncyness );
848         shard->physicsObj.SetFriction( 0.6f, 0.6f, friction );
849         shard->physicsObj.SetGravity( gameLocal.GetGravity() );
850         shard->physicsObj.SetContents( CONTENTS_RENDERMODEL );
851         shard->physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
852         shard->physicsObj.ApplyImpulse( 0, origin, impulse * linearVelocityScale * dir );
853         shard->physicsObj.SetAngularVelocity( dir.Cross( dir2 ) * ( f * angularVelocityScale ) );
854
855         shard->clipModel->SetId( clipModelId );
856
857         BecomeActive( TH_PHYSICS );
858 }
859
860 /*
861 ================
862 idBrittleFracture::Shatter
863 ================
864 */
865 void idBrittleFracture::Shatter( const idVec3 &point, const idVec3 &impulse, const int time ) {
866         int i;
867         idVec3 dir;
868         shard_t *shard;
869         float m;
870
871         if ( gameLocal.isServer ) {
872                 idBitMsg        msg;
873                 byte            msgBuf[MAX_EVENT_PARAM_SIZE];
874
875                 msg.Init( msgBuf, sizeof( msgBuf ) );
876                 msg.BeginWriting();
877                 msg.WriteFloat( point[0] );
878                 msg.WriteFloat( point[1] );
879                 msg.WriteFloat( point[2] );
880                 msg.WriteFloat( impulse[0] );
881                 msg.WriteFloat( impulse[1] );
882                 msg.WriteFloat( impulse[2] );
883                 ServerSendEvent( EVENT_SHATTER, &msg, true, -1 );
884         }
885
886         if ( time > ( gameLocal.time - SHARD_ALIVE_TIME ) ) {
887                 StartSound( "snd_shatter", SND_CHANNEL_ANY, 0, false, NULL );
888         }
889
890         if ( !IsBroken() ) {
891                 Break();
892         }
893
894         if ( fxFracture.Length() ) {
895                 idEntityFx::StartFx( fxFracture, &point, &GetPhysics()->GetAxis(), this, true );
896         }
897
898         dir = impulse;
899         m = dir.Normalize();
900
901         for ( i = 0; i < shards.Num(); i++ ) {
902                 shard = shards[i];
903
904                 if ( shard->droppedTime != -1 ) {
905                         continue;
906                 }
907
908                 if ( ( shard->clipModel->GetOrigin() - point ).LengthSqr() > Square( maxShatterRadius ) ) {
909                         continue;
910                 }
911
912                 DropShard( shard, point, dir, m, time );
913         }
914
915         DropFloatingIslands( point, impulse, time );
916 }
917
918 /*
919 ================
920 idBrittleFracture::DropFloatingIslands
921 ================
922 */
923 void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 &impulse, const int time ) {
924         int i, j, numIslands;
925         int queueStart, queueEnd;
926         shard_t *curShard, *nextShard, **queue;
927         bool touchesEdge;
928         idVec3 dir;
929
930         dir = impulse;
931         dir.Normalize();
932
933         numIslands = 0;
934         queue = (shard_t **) _alloca16( shards.Num() * sizeof(shard_t **) );
935         for ( i = 0; i < shards.Num(); i++ ) {
936                 shards[i]->islandNum = 0;
937         }
938
939         for ( i = 0; i < shards.Num(); i++ ) {
940
941                 if ( shards[i]->droppedTime != -1 ) {
942                         continue;
943                 }
944
945                 if ( shards[i]->islandNum ) {
946                         continue;
947                 }
948
949         queueStart = 0;
950                 queueEnd = 1;
951                 queue[0] = shards[i];
952                 shards[i]->islandNum = numIslands+1;
953                 touchesEdge = false;
954
955                 if ( shards[i]->atEdge ) {
956                         touchesEdge = true;
957                 }
958
959                 for ( curShard = queue[queueStart]; queueStart < queueEnd; curShard = queue[++queueStart] ) {
960
961                         for ( j = 0; j < curShard->neighbours.Num(); j++ ) {
962
963                                 nextShard = curShard->neighbours[j];
964
965                                 if ( nextShard->droppedTime != -1 ) {
966                                         continue;
967                                 }
968
969                                 if ( nextShard->islandNum ) {
970                                         continue;
971                                 }
972
973                                 queue[queueEnd++] = nextShard;
974                                 nextShard->islandNum = numIslands+1;
975
976                                 if ( nextShard->atEdge ) {
977                                         touchesEdge = true;
978                                 }
979                         }
980                 }
981                 numIslands++;
982
983                 // if the island is not connected to the world at any edges
984                 if ( !touchesEdge ) {
985                         for ( j = 0; j < queueEnd; j++ ) {
986                                 DropShard( queue[j], point, dir, 0.0f, time );
987                         }
988                 }
989         }
990 }
991
992 /*
993 ================
994 idBrittleFracture::Break
995 ================
996 */
997 void idBrittleFracture::Break( void ) {
998         fl.takedamage = false;
999         physicsObj.SetContents( CONTENTS_RENDERMODEL | CONTENTS_TRIGGER );
1000 }
1001
1002 /*
1003 ================
1004 idBrittleFracture::IsBroken
1005 ================
1006 */
1007 bool idBrittleFracture::IsBroken( void ) const {
1008         return ( fl.takedamage == false );
1009 }
1010
1011 /*
1012 ================
1013 idBrittleFracture::Killed
1014 ================
1015 */
1016 void idBrittleFracture::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
1017         if ( !disableFracture ) {
1018                 ActivateTargets( this );
1019                 Break();
1020         }
1021 }
1022
1023 /*
1024 ================
1025 idBrittleFracture::AddDamageEffect
1026 ================
1027 */
1028 void idBrittleFracture::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
1029         if ( !disableFracture ) {
1030                 ProjectDecal( collision.c.point, collision.c.normal, gameLocal.time, damageDefName );
1031         }
1032 }
1033
1034 /*
1035 ================
1036 idBrittleFracture::Fracture_r
1037 ================
1038 */
1039 void idBrittleFracture::Fracture_r( idFixedWinding &w ) {
1040         int i, j, bestPlane;
1041         float a, c, s, dist, bestDist;
1042         idVec3 origin;
1043         idPlane windingPlane, splitPlanes[2];
1044         idMat3 axis, axistemp;
1045         idFixedWinding back;
1046         idTraceModel trm;
1047         idClipModel *clipModel;
1048
1049         while( 1 ) {
1050                 origin = w.GetCenter();
1051                 w.GetPlane( windingPlane );
1052
1053                 if ( w.GetArea() < maxShardArea ) {
1054                         break;
1055                 }
1056
1057                 // randomly create a split plane
1058                 axis[2] = windingPlane.Normal();
1059 #ifdef _D3XP
1060                 if ( isXraySurface ) {
1061                         a = idMath::TWO_PI / 2.f;
1062                 }
1063                 else {
1064                         a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
1065                 }
1066 #else
1067                 a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
1068 #endif
1069                 c = cos( a );
1070                 s = -sin( a );
1071                 axis[2].NormalVectors( axistemp[0], axistemp[1] );
1072                 axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
1073                 axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
1074
1075                 // get the best split plane
1076                 bestDist = 0.0f;
1077                 bestPlane = 0;
1078                 for ( i = 0; i < 2; i++ ) {
1079                         splitPlanes[i].SetNormal( axis[i] );
1080                         splitPlanes[i].FitThroughPoint( origin );
1081                         for ( j = 0; j < w.GetNumPoints(); j++ ) {
1082                                 dist = splitPlanes[i].Distance( w[j].ToVec3() );
1083                                 if ( dist > bestDist ) {
1084                                         bestDist = dist;
1085                                         bestPlane = i;
1086                                 }
1087                         }
1088                 }
1089
1090                 // split the winding
1091                 if ( !w.Split( &back, splitPlanes[bestPlane] ) ) {
1092                         break;
1093                 }
1094
1095                 // recursively create shards for the back winding
1096                 Fracture_r( back );
1097         }
1098
1099         // translate the winding to it's center
1100         origin = w.GetCenter();
1101         for ( j = 0; j < w.GetNumPoints(); j++ ) {
1102                 w[j].ToVec3() -= origin;
1103         }
1104         w.RemoveEqualPoints();
1105
1106         trm.SetupPolygon( w );
1107         trm.Shrink( CM_CLIP_EPSILON );
1108         clipModel = new idClipModel( trm );
1109
1110         physicsObj.SetClipModel( clipModel, 1.0f, shards.Num() );
1111         physicsObj.SetOrigin( GetPhysics()->GetOrigin() + origin, shards.Num() );
1112         physicsObj.SetAxis( GetPhysics()->GetAxis(), shards.Num() );
1113
1114         AddShard( clipModel, w );
1115 }
1116
1117 /*
1118 ================
1119 idBrittleFracture::CreateFractures
1120 ================
1121 */
1122 void idBrittleFracture::CreateFractures( const idRenderModel *renderModel ) {
1123         int i, j, k;
1124         const modelSurface_t *surf;
1125         const idDrawVert *v;
1126         idFixedWinding w;
1127
1128         if ( !renderModel ) {
1129                 return;
1130         }
1131
1132         physicsObj.SetSelf( this );
1133         physicsObj.SetOrigin( GetPhysics()->GetOrigin(), 0 );
1134         physicsObj.SetAxis( GetPhysics()->GetAxis(), 0 );
1135
1136 #ifdef _D3XP
1137         if ( isXraySurface ) {
1138                 for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
1139                         surf = renderModel->Surface( i );
1140                         material = surf->shader;
1141
1142                         w.Clear();
1143
1144                         int k = 0;
1145                         v = &surf->geometry->verts[k];
1146                         w.AddPoint( v->xyz );
1147                         w[k].s = v->st[0];
1148                         w[k].t = v->st[1];
1149
1150                         k = 1;
1151                         v = &surf->geometry->verts[k];
1152                         w.AddPoint( v->xyz );
1153                         w[k].s = v->st[0];
1154                         w[k].t = v->st[1];
1155
1156                         k = 3;
1157                         v = &surf->geometry->verts[k];
1158                         w.AddPoint( v->xyz );
1159                         w[k].s = v->st[0];
1160                         w[k].t = v->st[1];
1161
1162                         k = 2;
1163                         v = &surf->geometry->verts[k];
1164                         w.AddPoint( v->xyz );
1165                         w[k].s = v->st[0];
1166                         w[k].t = v->st[1];
1167
1168                         Fracture_r( w );
1169                 }
1170
1171         }
1172         else {
1173                 for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
1174                         surf = renderModel->Surface( i );
1175                         material = surf->shader;
1176
1177                         for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
1178                                 w.Clear();
1179                                 for ( k = 0; k < 3; k++ ) {
1180                                         v = &surf->geometry->verts[ surf->geometry->indexes[ j + 2 - k ] ];
1181                                         w.AddPoint( v->xyz );
1182                                         w[k].s = v->st[0];
1183                                         w[k].t = v->st[1];
1184                                 }
1185                                 Fracture_r( w );
1186                         }
1187                 }
1188         }
1189 #else
1190         for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
1191                 surf = renderModel->Surface( i );
1192                 material = surf->shader;
1193
1194                 for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
1195                         w.Clear();
1196                         for ( k = 0; k < 3; k++ ) {
1197                                 v = &surf->geometry->verts[ surf->geometry->indexes[ j + 2 - k ] ];
1198                                 w.AddPoint( v->xyz );
1199                                 w[k].s = v->st[0];
1200                                 w[k].t = v->st[1];
1201                         }
1202                         Fracture_r( w );
1203                 }
1204         }
1205 #endif
1206
1207         physicsObj.SetContents( material->GetContentFlags() );
1208         SetPhysics( &physicsObj );
1209 }
1210
1211 /*
1212 ================
1213 idBrittleFracture::FindNeighbours
1214 ================
1215 */
1216 void idBrittleFracture::FindNeighbours( void ) {
1217         int i, j, k, l;
1218         idVec3 p1, p2, dir;
1219         idMat3 axis;
1220         idPlane plane[4];
1221
1222         for ( i = 0; i < shards.Num(); i++ ) {
1223
1224                 shard_t *shard1 = shards[i];
1225                 const idWinding &w1 = shard1->winding;
1226                 const idVec3 &origin1 = shard1->clipModel->GetOrigin();
1227                 const idMat3 &axis1 = shard1->clipModel->GetAxis();
1228
1229                 for ( k = 0; k < w1.GetNumPoints(); k++ ) {
1230
1231                         p1 = origin1 + w1[k].ToVec3() * axis1;
1232                         p2 = origin1 + w1[(k+1)%w1.GetNumPoints()].ToVec3() * axis1;
1233                         dir = p2 - p1;
1234                         dir.Normalize();
1235                         axis = dir.ToMat3();
1236
1237                         plane[0].SetNormal( dir );
1238                         plane[0].FitThroughPoint( p1 );
1239                         plane[1].SetNormal( -dir );
1240                         plane[1].FitThroughPoint( p2 );
1241                         plane[2].SetNormal( axis[1] );
1242                         plane[2].FitThroughPoint( p1 );
1243                         plane[3].SetNormal( axis[2] );
1244                         plane[3].FitThroughPoint( p1 );
1245
1246                         for ( j = 0; j < shards.Num(); j++ ) {
1247
1248                                 if ( i == j ) {
1249                                         continue;
1250                                 }
1251
1252                                 shard_t *shard2 = shards[j];
1253
1254                                 for ( l = 0; l < shard1->neighbours.Num(); l++ ) {
1255                                         if ( shard1->neighbours[l] == shard2 ) {
1256                                                 break;
1257                                         }
1258                                 }
1259                                 if ( l < shard1->neighbours.Num() ) {
1260                                         continue;
1261                                 }
1262
1263                                 const idWinding &w2 = shard2->winding;
1264                                 const idVec3 &origin2 = shard2->clipModel->GetOrigin();
1265                                 const idMat3 &axis2 = shard2->clipModel->GetAxis();
1266
1267                                 for ( l = w2.GetNumPoints()-1; l >= 0; l-- ) {
1268                                         p1 = origin2 + w2[l].ToVec3() * axis2;
1269                                         p2 = origin2 + w2[(l-1+w2.GetNumPoints())%w2.GetNumPoints()].ToVec3() * axis2;
1270                                         if ( plane[0].Side( p2, 0.1f ) == SIDE_FRONT && plane[1].Side( p1, 0.1f ) == SIDE_FRONT ) {
1271                                                 if ( plane[2].Side( p1, 0.1f ) == SIDE_ON && plane[3].Side( p1, 0.1f ) == SIDE_ON ) {
1272                                                         if ( plane[2].Side( p2, 0.1f ) == SIDE_ON && plane[3].Side( p2, 0.1f ) == SIDE_ON ) {
1273                                                                 shard1->neighbours.Append( shard2 );
1274                                                                 shard1->edgeHasNeighbour[k] = true;
1275                                                                 shard2->neighbours.Append( shard1 );
1276                                                                 shard2->edgeHasNeighbour[(l-1+w2.GetNumPoints())%w2.GetNumPoints()] = true;
1277                                                                 break;
1278                                                         }
1279                                                 }
1280                                         }
1281                                 }
1282                         }
1283                 }
1284
1285                 for ( k = 0; k < w1.GetNumPoints(); k++ ) {
1286                         if ( !shard1->edgeHasNeighbour[k] ) {
1287                                 break;
1288                         }
1289                 }
1290                 if ( k < w1.GetNumPoints() ) {
1291                         shard1->atEdge = true;
1292                 } else {
1293                         shard1->atEdge = false;
1294                 }
1295         }
1296 }
1297
1298 /*
1299 ================
1300 idBrittleFracture::Event_Activate
1301 ================
1302 */
1303 void idBrittleFracture::Event_Activate( idEntity *activator ) {
1304         disableFracture = false;
1305         if ( health <= 0 ) {
1306                 Break();
1307         }
1308 }
1309
1310 /*
1311 ================
1312 idBrittleFracture::Event_Touch
1313 ================
1314 */
1315 void idBrittleFracture::Event_Touch( idEntity *other, trace_t *trace ) {
1316         idVec3 point, impulse;
1317
1318         if ( !IsBroken() ) {
1319                 return;
1320         }
1321
1322         if ( trace->c.id < 0 || trace->c.id >= shards.Num() ) {
1323                 return;
1324         }
1325
1326         point = shards[trace->c.id]->clipModel->GetOrigin();
1327         impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass();
1328
1329         Shatter( point, impulse, gameLocal.time );
1330 }
1331
1332 /*
1333 ================
1334 idBrittleFracture::ClientPredictionThink
1335 ================
1336 */
1337 void idBrittleFracture::ClientPredictionThink( void ) {
1338         // only think forward because the state is not synced through snapshots
1339         if ( !gameLocal.isNewFrame ) {
1340                 return;
1341         }
1342
1343         Think();
1344 }
1345
1346 /*
1347 ================
1348 idBrittleFracture::ClientReceiveEvent
1349 ================
1350 */
1351 bool idBrittleFracture::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
1352         idVec3 point, dir;
1353
1354         switch( event ) {
1355                 case EVENT_PROJECT_DECAL: {
1356                         point[0] = msg.ReadFloat();
1357                         point[1] = msg.ReadFloat();
1358                         point[2] = msg.ReadFloat();
1359                         dir[0] = msg.ReadFloat();
1360                         dir[1] = msg.ReadFloat();
1361                         dir[2] = msg.ReadFloat();
1362                         ProjectDecal( point, dir, time, NULL );
1363                         return true;
1364                 }
1365                 case EVENT_SHATTER: {
1366                         point[0] = msg.ReadFloat();
1367                         point[1] = msg.ReadFloat();
1368                         point[2] = msg.ReadFloat();
1369                         dir[0] = msg.ReadFloat();
1370                         dir[1] = msg.ReadFloat();
1371                         dir[2] = msg.ReadFloat();
1372                         Shatter( point, dir, time );
1373                         return true;
1374                 }
1375                 default: {
1376                         return idEntity::ClientReceiveEvent( event, time, msg );
1377                 }
1378         }
1379         return false;
1380 }