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