1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- CLASS_DECLARATION( idEntity, idBrittleFracture )
- EVENT( EV_Activate, idBrittleFracture::Event_Activate )
- EVENT( EV_Touch, idBrittleFracture::Event_Touch )
- END_CLASS
- const int SHARD_ALIVE_TIME = 5000;
- const int SHARD_FADE_START = 2000;
- static const char *brittleFracture_SnapshotName = "_BrittleFracture_Snapshot_";
- /*
- ================
- idBrittleFracture::idBrittleFracture
- ================
- */
- idBrittleFracture::idBrittleFracture( void ) {
- material = NULL;
- decalMaterial = NULL;
- decalSize = 0.0f;
- maxShardArea = 0.0f;
- maxShatterRadius = 0.0f;
- minShatterRadius = 0.0f;
- linearVelocityScale = 0.0f;
- angularVelocityScale = 0.0f;
- shardMass = 0.0f;
- density = 0.0f;
- friction = 0.0f;
- bouncyness = 0.0f;
- fxFracture.Clear();
- bounds.Clear();
- disableFracture = false;
- lastRenderEntityUpdate = -1;
- changed = false;
- fl.networkSync = true;
- #ifdef _D3XP
- isXraySurface = false;
- #endif
- }
- /*
- ================
- idBrittleFracture::~idBrittleFracture
- ================
- */
- idBrittleFracture::~idBrittleFracture( void ) {
- int i;
- for ( i = 0; i < shards.Num(); i++ ) {
- shards[i]->decals.DeleteContents( true );
- delete shards[i];
- }
- // make sure the render entity is freed before the model is freed
- FreeModelDef();
- renderModelManager->FreeModel( renderEntity.hModel );
- }
- /*
- ================
- idBrittleFracture::Save
- ================
- */
- void idBrittleFracture::Save( idSaveGame *savefile ) const {
- int i, j;
- savefile->WriteInt( health );
- entityFlags_s flags = fl;
- LittleBitField( &flags, sizeof( flags ) );
- savefile->Write( &flags, sizeof( flags ) );
-
- // setttings
- savefile->WriteMaterial( material );
- savefile->WriteMaterial( decalMaterial );
- savefile->WriteFloat( decalSize );
- savefile->WriteFloat( maxShardArea );
- savefile->WriteFloat( maxShatterRadius );
- savefile->WriteFloat( minShatterRadius );
- savefile->WriteFloat( linearVelocityScale );
- savefile->WriteFloat( angularVelocityScale );
- savefile->WriteFloat( shardMass );
- savefile->WriteFloat( density );
- savefile->WriteFloat( friction );
- savefile->WriteFloat( bouncyness );
- savefile->WriteString( fxFracture );
- // state
- savefile->WriteBounds( bounds );
- savefile->WriteBool( disableFracture );
- savefile->WriteInt( lastRenderEntityUpdate );
- savefile->WriteBool( changed );
- savefile->WriteStaticObject( physicsObj );
- savefile->WriteInt( shards.Num() );
- for ( i = 0; i < shards.Num(); i++ ) {
- savefile->WriteWinding( shards[i]->winding );
- savefile->WriteInt( shards[i]->decals.Num() );
- for ( j = 0; j < shards[i]->decals.Num(); j++ ) {
- savefile->WriteWinding( *shards[i]->decals[j] );
- }
- savefile->WriteInt( shards[i]->neighbours.Num() );
- for ( j = 0; j < shards[i]->neighbours.Num(); j++ ) {
- int index = shards.FindIndex(shards[i]->neighbours[j]);
- assert(index != -1);
- savefile->WriteInt( index );
- }
- savefile->WriteInt( shards[i]->edgeHasNeighbour.Num() );
- for ( j = 0; j < shards[i]->edgeHasNeighbour.Num(); j++ ) {
- savefile->WriteBool( shards[i]->edgeHasNeighbour[j] );
- }
- savefile->WriteInt( shards[i]->droppedTime );
- savefile->WriteInt( shards[i]->islandNum );
- savefile->WriteBool( shards[i]->atEdge );
- savefile->WriteStaticObject( shards[i]->physicsObj );
- }
- #ifdef _D3XP
- savefile->WriteBool( isXraySurface );
- #endif
- }
- /*
- ================
- idBrittleFracture::Restore
- ================
- */
- void idBrittleFracture::Restore( idRestoreGame *savefile ) {
- int i, j , num;
- renderEntity.hModel = renderModelManager->AllocModel();
- renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
- renderEntity.callback = idBrittleFracture::ModelCallback;
- renderEntity.noShadow = true;
- renderEntity.noSelfShadow = true;
- renderEntity.noDynamicInteractions = false;
- savefile->ReadInt( health );
- savefile->Read( &fl, sizeof( fl ) );
- LittleBitField( &fl, sizeof( fl ) );
- // setttings
- savefile->ReadMaterial( material );
- savefile->ReadMaterial( decalMaterial );
- savefile->ReadFloat( decalSize );
- savefile->ReadFloat( maxShardArea );
- savefile->ReadFloat( maxShatterRadius );
- savefile->ReadFloat( minShatterRadius );
- savefile->ReadFloat( linearVelocityScale );
- savefile->ReadFloat( angularVelocityScale );
- savefile->ReadFloat( shardMass );
- savefile->ReadFloat( density );
- savefile->ReadFloat( friction );
- savefile->ReadFloat( bouncyness );
- savefile->ReadString( fxFracture );
- // state
- savefile->ReadBounds(bounds);
- savefile->ReadBool( disableFracture );
- savefile->ReadInt( lastRenderEntityUpdate );
- savefile->ReadBool( changed );
- savefile->ReadStaticObject( physicsObj );
- RestorePhysics( &physicsObj );
- savefile->ReadInt( num );
- shards.SetNum( num );
- for ( i = 0; i < num; i++ ) {
- shards[i] = new shard_t;
- }
- for ( i = 0; i < num; i++ ) {
- savefile->ReadWinding( shards[i]->winding );
- savefile->ReadInt( j );
- shards[i]->decals.SetNum( j );
- for ( j = 0; j < shards[i]->decals.Num(); j++ ) {
- shards[i]->decals[j] = new idFixedWinding;
- savefile->ReadWinding( *shards[i]->decals[j] );
- }
- savefile->ReadInt( j );
- shards[i]->neighbours.SetNum( j );
- for ( j = 0; j < shards[i]->neighbours.Num(); j++ ) {
- int index;
- savefile->ReadInt( index );
- assert(index != -1);
- shards[i]->neighbours[j] = shards[index];
- }
- savefile->ReadInt( j );
- shards[i]->edgeHasNeighbour.SetNum( j );
- for ( j = 0; j < shards[i]->edgeHasNeighbour.Num(); j++ ) {
- savefile->ReadBool( shards[i]->edgeHasNeighbour[j] );
- }
- savefile->ReadInt( shards[i]->droppedTime );
- savefile->ReadInt( shards[i]->islandNum );
- savefile->ReadBool( shards[i]->atEdge );
- savefile->ReadStaticObject( shards[i]->physicsObj );
- if ( shards[i]->droppedTime < 0 ) {
- shards[i]->clipModel = physicsObj.GetClipModel( i );
- } else {
- shards[i]->clipModel = shards[i]->physicsObj.GetClipModel();
- }
- }
- #ifdef _D3XP
- savefile->ReadBool( isXraySurface );
- #endif
- }
- /*
- ================
- idBrittleFracture::Spawn
- ================
- */
- void idBrittleFracture::Spawn( void ) {
- // get shard properties
- decalMaterial = declManager->FindMaterial( spawnArgs.GetString( "mtr_decal" ) );
- decalSize = spawnArgs.GetFloat( "decalSize", "40" );
- maxShardArea = spawnArgs.GetFloat( "maxShardArea", "200" );
- maxShardArea = idMath::ClampFloat( 100, 10000, maxShardArea );
- maxShatterRadius = spawnArgs.GetFloat( "maxShatterRadius", "40" );
- minShatterRadius = spawnArgs.GetFloat( "minShatterRadius", "10" );
- linearVelocityScale = spawnArgs.GetFloat( "linearVelocityScale", "0.1" );
- angularVelocityScale = spawnArgs.GetFloat( "angularVelocityScale", "40" );
- fxFracture = spawnArgs.GetString( "fx" );
- // get rigid body properties
- shardMass = spawnArgs.GetFloat( "shardMass", "20" );
- shardMass = idMath::ClampFloat( 0.001f, 1000.0f, shardMass );
- spawnArgs.GetFloat( "density", "0.1", density );
- density = idMath::ClampFloat( 0.001f, 1000.0f, density );
- spawnArgs.GetFloat( "friction", "0.4", friction );
- friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
- spawnArgs.GetFloat( "bouncyness", "0.01", bouncyness );
- bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
- disableFracture = spawnArgs.GetBool( "disableFracture", "0" );
- health = spawnArgs.GetInt( "health", "40" );
- fl.takedamage = true;
- // FIXME: set "bleed" so idProjectile calls AddDamageEffect
- spawnArgs.SetBool( "bleed", 1 );
- #ifdef _D3XP
- // check for xray surface
- if ( 1 ) {
- const idRenderModel *model = renderEntity.hModel;
- isXraySurface = false;
- for ( int i = 0; i < model->NumSurfaces(); i++ ) {
- const modelSurface_t *surf = model->Surface( i );
- if ( idStr( surf->shader->GetName() ) == "textures/smf/window_scratch" ) {
- isXraySurface = true;
- break;
- }
- }
- }
- #endif
- CreateFractures( renderEntity.hModel );
- FindNeighbours();
- renderEntity.hModel = renderModelManager->AllocModel();
- renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
- renderEntity.callback = idBrittleFracture::ModelCallback;
- renderEntity.noShadow = true;
- renderEntity.noSelfShadow = true;
- renderEntity.noDynamicInteractions = false;
- }
- /*
- ================
- idBrittleFracture::AddShard
- ================
- */
- void idBrittleFracture::AddShard( idClipModel *clipModel, idFixedWinding &w ) {
- shard_t *shard = new shard_t;
- shard->clipModel = clipModel;
- shard->droppedTime = -1;
- shard->winding = w;
- shard->decals.Clear();
- shard->edgeHasNeighbour.AssureSize( w.GetNumPoints(), false );
- shard->neighbours.Clear();
- shard->atEdge = false;
- shards.Append( shard );
- }
- /*
- ================
- idBrittleFracture::RemoveShard
- ================
- */
- void idBrittleFracture::RemoveShard( int index ) {
- int i;
- delete shards[index];
- shards.RemoveIndex( index );
- physicsObj.RemoveIndex( index );
- for ( i = index; i < shards.Num(); i++ ) {
- shards[i]->clipModel->SetId( i );
- }
- }
- /*
- ================
- idBrittleFracture::UpdateRenderEntity
- ================
- */
- bool idBrittleFracture::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
- int i, j, k, n, msec, numTris, numDecalTris;
- float fade;
- dword packedColor;
- srfTriangles_t *tris, *decalTris;
- modelSurface_t surface;
- idDrawVert *v;
- idPlane plane;
- idMat3 tangents;
- // this may be triggered by a model trace or other non-view related source,
- // to which we should look like an empty model
- if ( !renderView ) {
- return false;
- }
- // don't regenerate it if it is current
- if ( lastRenderEntityUpdate == gameLocal.time || !changed ) {
- return false;
- }
- lastRenderEntityUpdate = gameLocal.time;
- changed = false;
- numTris = 0;
- numDecalTris = 0;
- for ( i = 0; i < shards.Num(); i++ ) {
- n = shards[i]->winding.GetNumPoints();
- if ( n > 2 ) {
- numTris += n - 2;
- }
- for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
- n = shards[i]->decals[k]->GetNumPoints();
- if ( n > 2 ) {
- numDecalTris += n - 2;
- }
- }
- }
- // FIXME: re-use model surfaces
- renderEntity->hModel->InitEmpty( brittleFracture_SnapshotName );
- // allocate triangle surfaces for the fractures and decals
- tris = renderEntity->hModel->AllocSurfaceTriangles( numTris * 3, material->ShouldCreateBackSides() ? numTris * 6 : numTris * 3 );
- decalTris = renderEntity->hModel->AllocSurfaceTriangles( numDecalTris * 3, decalMaterial->ShouldCreateBackSides() ? numDecalTris * 6 : numDecalTris * 3 );
- for ( i = 0; i < shards.Num(); i++ ) {
- const idVec3 &origin = shards[i]->clipModel->GetOrigin();
- const idMat3 &axis = shards[i]->clipModel->GetAxis();
- fade = 1.0f;
- if ( shards[i]->droppedTime >= 0 ) {
- msec = gameLocal.time - shards[i]->droppedTime - SHARD_FADE_START;
- if ( msec > 0 ) {
- fade = 1.0f - (float) msec / ( SHARD_ALIVE_TIME - SHARD_FADE_START );
- }
- }
- packedColor = PackColor( idVec4( renderEntity->shaderParms[ SHADERPARM_RED ] * fade,
- renderEntity->shaderParms[ SHADERPARM_GREEN ] * fade,
- renderEntity->shaderParms[ SHADERPARM_BLUE ] * fade,
- fade ) );
- const idWinding &winding = shards[i]->winding;
- winding.GetPlane( plane );
- tangents = ( plane.Normal() * axis ).ToMat3();
- for ( j = 2; j < winding.GetNumPoints(); j++ ) {
- v = &tris->verts[tris->numVerts++];
- v->Clear();
- v->xyz = origin + winding[0].ToVec3() * axis;
- v->st[0] = winding[0].s;
- v->st[1] = winding[0].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- v = &tris->verts[tris->numVerts++];
- v->Clear();
- v->xyz = origin + winding[j-1].ToVec3() * axis;
- v->st[0] = winding[j-1].s;
- v->st[1] = winding[j-1].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- v = &tris->verts[tris->numVerts++];
- v->Clear();
- v->xyz = origin + winding[j].ToVec3() * axis;
- v->st[0] = winding[j].s;
- v->st[1] = winding[j].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
- tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
- tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
- if ( material->ShouldCreateBackSides() ) {
- tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
- tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
- tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
- }
- }
- for ( k = 0; k < shards[i]->decals.Num(); k++ ) {
- const idWinding &decalWinding = *shards[i]->decals[k];
- for ( j = 2; j < decalWinding.GetNumPoints(); j++ ) {
- v = &decalTris->verts[decalTris->numVerts++];
- v->Clear();
- v->xyz = origin + decalWinding[0].ToVec3() * axis;
- v->st[0] = decalWinding[0].s;
- v->st[1] = decalWinding[0].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- v = &decalTris->verts[decalTris->numVerts++];
- v->Clear();
- v->xyz = origin + decalWinding[j-1].ToVec3() * axis;
- v->st[0] = decalWinding[j-1].s;
- v->st[1] = decalWinding[j-1].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- v = &decalTris->verts[decalTris->numVerts++];
- v->Clear();
- v->xyz = origin + decalWinding[j].ToVec3() * axis;
- v->st[0] = decalWinding[j].s;
- v->st[1] = decalWinding[j].t;
- v->normal = tangents[0];
- v->tangents[0] = tangents[1];
- v->tangents[1] = tangents[2];
- v->SetColor( packedColor );
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
- if ( decalMaterial->ShouldCreateBackSides() ) {
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
- decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
- }
- }
- }
- }
- tris->tangentsCalculated = true;
- decalTris->tangentsCalculated = true;
- SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts );
- SIMDProcessor->MinMax( decalTris->bounds[0], decalTris->bounds[1], decalTris->verts, decalTris->numVerts );
- memset( &surface, 0, sizeof( surface ) );
- surface.shader = material;
- surface.id = 0;
- surface.geometry = tris;
- renderEntity->hModel->AddSurface( surface );
- memset( &surface, 0, sizeof( surface ) );
- surface.shader = decalMaterial;
- surface.id = 1;
- surface.geometry = decalTris;
- renderEntity->hModel->AddSurface( surface );
- return true;
- }
- /*
- ================
- idBrittleFracture::ModelCallback
- ================
- */
- bool idBrittleFracture::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
- const idBrittleFracture *ent;
- ent = static_cast<idBrittleFracture *>(gameLocal.entities[ renderEntity->entityNum ]);
- if ( !ent ) {
- gameLocal.Error( "idBrittleFracture::ModelCallback: callback with NULL game entity" );
- }
- return ent->UpdateRenderEntity( renderEntity, renderView );
- }
- /*
- ================
- idBrittleFracture::Present
- ================
- */
- void idBrittleFracture::Present() {
- // don't present to the renderer if the entity hasn't changed
- if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
- return;
- }
- BecomeInactive( TH_UPDATEVISUALS );
- renderEntity.bounds = bounds;
- renderEntity.origin.Zero();
- renderEntity.axis.Identity();
- // force an update because the bounds/origin/axis may stay the same while the model changes
- renderEntity.forceUpdate = true;
- // add to refresh list
- if ( modelDefHandle == -1 ) {
- modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
- } else {
- gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
- }
- changed = true;
- }
- /*
- ================
- idBrittleFracture::Think
- ================
- */
- void idBrittleFracture::Think( void ) {
- int i, startTime, endTime, droppedTime;
- shard_t *shard;
- bool atRest = true, fading = false;
- // remove overdue shards
- for ( i = 0; i < shards.Num(); i++ ) {
- droppedTime = shards[i]->droppedTime;
- if ( droppedTime != -1 ) {
- if ( gameLocal.time - droppedTime > SHARD_ALIVE_TIME ) {
- RemoveShard( i );
- i--;
- }
- fading = true;
- }
- }
- // remove the entity when nothing is visible
- if ( !shards.Num() ) {
- PostEventMS( &EV_Remove, 0 );
- return;
- }
- if ( thinkFlags & TH_PHYSICS ) {
- startTime = gameLocal.previousTime;
- endTime = gameLocal.time;
- // run physics on shards
- for ( i = 0; i < shards.Num(); i++ ) {
- shard = shards[i];
- if ( shard->droppedTime == -1 ) {
- continue;
- }
- shard->physicsObj.Evaluate( endTime - startTime, endTime );
- if ( !shard->physicsObj.IsAtRest() ) {
- atRest = false;
- }
- }
- if ( atRest ) {
- BecomeInactive( TH_PHYSICS );
- } else {
- BecomeActive( TH_PHYSICS );
- }
- }
- if ( !atRest || bounds.IsCleared() ) {
- bounds.Clear();
- for ( i = 0; i < shards.Num(); i++ ) {
- bounds.AddBounds( shards[i]->clipModel->GetAbsBounds() );
- }
- }
- if ( fading ) {
- BecomeActive( TH_UPDATEVISUALS | TH_THINK );
- } else {
- BecomeInactive( TH_THINK );
- }
- RunPhysics();
- Present();
- }
- /*
- ================
- idBrittleFracture::ApplyImpulse
- ================
- */
- void idBrittleFracture::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
- if ( id < 0 || id >= shards.Num() ) {
- return;
- }
- if ( shards[id]->droppedTime != -1 ) {
- shards[id]->physicsObj.ApplyImpulse( 0, point, impulse );
- } else if ( health <= 0 && !disableFracture ) {
- Shatter( point, impulse, gameLocal.time );
- }
- }
- /*
- ================
- idBrittleFracture::AddForce
- ================
- */
- void idBrittleFracture::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
- if ( id < 0 || id >= shards.Num() ) {
- return;
- }
- if ( shards[id]->droppedTime != -1 ) {
- shards[id]->physicsObj.AddForce( 0, point, force );
- } else if ( health <= 0 && !disableFracture ) {
- Shatter( point, force, gameLocal.time );
- }
- }
- /*
- ================
- idBrittleFracture::ProjectDecal
- ================
- */
- void idBrittleFracture::ProjectDecal( const idVec3 &point, const idVec3 &dir, const int time, const char *damageDefName ) {
- int i, j, bits, clipBits;
- float a, c, s;
- idVec2 st[MAX_POINTS_ON_WINDING];
- idVec3 origin;
- idMat3 axis, axistemp;
- idPlane textureAxis[2];
- if ( gameLocal.isServer ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.BeginWriting();
- msg.WriteFloat( point[0] );
- msg.WriteFloat( point[1] );
- msg.WriteFloat( point[2] );
- msg.WriteFloat( dir[0] );
- msg.WriteFloat( dir[1] );
- msg.WriteFloat( dir[2] );
- ServerSendEvent( EVENT_PROJECT_DECAL, &msg, true, -1 );
- }
- if ( time >= gameLocal.time ) {
- // try to get the sound from the damage def
- const idDeclEntityDef *damageDef = NULL;
- const idSoundShader *sndShader = NULL;
- if ( damageDefName ) {
- damageDef = gameLocal.FindEntityDef( damageDefName, false );
- if ( damageDef ) {
- sndShader = declManager->FindSound( damageDef->dict.GetString( "snd_shatter", "" ) );
- }
- }
- if ( sndShader ) {
- StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL );
- } else {
- StartSound( "snd_bullethole", SND_CHANNEL_ANY, 0, false, NULL );
- }
- }
- a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
- c = cos( a );
- s = -sin( a );
- axis[2] = -dir;
- axis[2].Normalize();
- axis[2].NormalVectors( axistemp[0], axistemp[1] );
- axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
- axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
- textureAxis[0] = axis[0] * ( 1.0f / decalSize );
- textureAxis[0][3] = -( point * textureAxis[0].Normal() ) + 0.5f;
- textureAxis[1] = axis[1] * ( 1.0f / decalSize );
- textureAxis[1][3] = -( point * textureAxis[1].Normal() ) + 0.5f;
- for ( i = 0; i < shards.Num(); i++ ) {
- idFixedWinding &winding = shards[i]->winding;
- origin = shards[i]->clipModel->GetOrigin();
- axis = shards[i]->clipModel->GetAxis();
- float d0, d1;
- clipBits = -1;
- for ( j = 0; j < winding.GetNumPoints(); j++ ) {
- idVec3 p = origin + winding[j].ToVec3() * axis;
- st[j].x = d0 = textureAxis[0].Distance( p );
- st[j].y = d1 = textureAxis[1].Distance( p );
- bits = FLOATSIGNBITSET( d0 );
- d0 = 1.0f - d0;
- bits |= FLOATSIGNBITSET( d1 ) << 2;
- d1 = 1.0f - d1;
- bits |= FLOATSIGNBITSET( d0 ) << 1;
- bits |= FLOATSIGNBITSET( d1 ) << 3;
- clipBits &= bits;
- }
- if ( clipBits ) {
- continue;
- }
- idFixedWinding *decal = new idFixedWinding;
- shards[i]->decals.Append( decal );
- decal->SetNumPoints( winding.GetNumPoints() );
- for ( j = 0; j < winding.GetNumPoints(); j++ ) {
- (*decal)[j].ToVec3() = winding[j].ToVec3();
- (*decal)[j].s = st[j].x;
- (*decal)[j].t = st[j].y;
- }
- }
- BecomeActive( TH_UPDATEVISUALS );
- }
- /*
- ================
- idBrittleFracture::DropShard
- ================
- */
- void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const idVec3 &dir, const float impulse, const int time ) {
- int i, j, clipModelId;
- float dist, f;
- idVec3 dir2, origin;
- idMat3 axis;
- shard_t *neighbour;
- // don't display decals on dropped shards
- shard->decals.DeleteContents( true );
- // remove neighbour pointers of neighbours pointing to this shard
- for ( i = 0; i < shard->neighbours.Num(); i++ ) {
- neighbour = shard->neighbours[i];
- for ( j = 0; j < neighbour->neighbours.Num(); j++ ) {
- if ( neighbour->neighbours[j] == shard ) {
- neighbour->neighbours.RemoveIndex( j );
- break;
- }
- }
- }
- // remove neighbour pointers
- shard->neighbours.Clear();
- // remove the clip model from the static physics object
- clipModelId = shard->clipModel->GetId();
- physicsObj.SetClipModel( NULL, 1.0f, clipModelId, false );
- origin = shard->clipModel->GetOrigin();
- axis = shard->clipModel->GetAxis();
- // set the dropped time for fading
- shard->droppedTime = time;
- dir2 = origin - point;
- dist = dir2.Normalize();
- f = dist > maxShatterRadius ? 1.0f : idMath::Sqrt( dist - minShatterRadius ) * ( 1.0f / idMath::Sqrt( maxShatterRadius - minShatterRadius ) );
- // setup the physics
- shard->physicsObj.SetSelf( this );
- shard->physicsObj.SetClipModel( shard->clipModel, density );
- shard->physicsObj.SetMass( shardMass );
- shard->physicsObj.SetOrigin( origin );
- shard->physicsObj.SetAxis( axis );
- shard->physicsObj.SetBouncyness( bouncyness );
- shard->physicsObj.SetFriction( 0.6f, 0.6f, friction );
- shard->physicsObj.SetGravity( gameLocal.GetGravity() );
- shard->physicsObj.SetContents( CONTENTS_RENDERMODEL );
- shard->physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
- shard->physicsObj.ApplyImpulse( 0, origin, impulse * linearVelocityScale * dir );
- shard->physicsObj.SetAngularVelocity( dir.Cross( dir2 ) * ( f * angularVelocityScale ) );
- shard->clipModel->SetId( clipModelId );
- BecomeActive( TH_PHYSICS );
- }
- /*
- ================
- idBrittleFracture::Shatter
- ================
- */
- void idBrittleFracture::Shatter( const idVec3 &point, const idVec3 &impulse, const int time ) {
- int i;
- idVec3 dir;
- shard_t *shard;
- float m;
- if ( gameLocal.isServer ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.BeginWriting();
- msg.WriteFloat( point[0] );
- msg.WriteFloat( point[1] );
- msg.WriteFloat( point[2] );
- msg.WriteFloat( impulse[0] );
- msg.WriteFloat( impulse[1] );
- msg.WriteFloat( impulse[2] );
- ServerSendEvent( EVENT_SHATTER, &msg, true, -1 );
- }
- if ( time > ( gameLocal.time - SHARD_ALIVE_TIME ) ) {
- StartSound( "snd_shatter", SND_CHANNEL_ANY, 0, false, NULL );
- }
- if ( !IsBroken() ) {
- Break();
- }
- if ( fxFracture.Length() ) {
- idEntityFx::StartFx( fxFracture, &point, &GetPhysics()->GetAxis(), this, true );
- }
- dir = impulse;
- m = dir.Normalize();
- for ( i = 0; i < shards.Num(); i++ ) {
- shard = shards[i];
- if ( shard->droppedTime != -1 ) {
- continue;
- }
- if ( ( shard->clipModel->GetOrigin() - point ).LengthSqr() > Square( maxShatterRadius ) ) {
- continue;
- }
- DropShard( shard, point, dir, m, time );
- }
- DropFloatingIslands( point, impulse, time );
- }
- /*
- ================
- idBrittleFracture::DropFloatingIslands
- ================
- */
- void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 &impulse, const int time ) {
- int i, j, numIslands;
- int queueStart, queueEnd;
- shard_t *curShard, *nextShard, **queue;
- bool touchesEdge;
- idVec3 dir;
- dir = impulse;
- dir.Normalize();
- numIslands = 0;
- queue = (shard_t **) _alloca16( shards.Num() * sizeof(shard_t **) );
- for ( i = 0; i < shards.Num(); i++ ) {
- shards[i]->islandNum = 0;
- }
- for ( i = 0; i < shards.Num(); i++ ) {
- if ( shards[i]->droppedTime != -1 ) {
- continue;
- }
- if ( shards[i]->islandNum ) {
- continue;
- }
- queueStart = 0;
- queueEnd = 1;
- queue[0] = shards[i];
- shards[i]->islandNum = numIslands+1;
- touchesEdge = false;
- if ( shards[i]->atEdge ) {
- touchesEdge = true;
- }
- for ( curShard = queue[queueStart]; queueStart < queueEnd; curShard = queue[++queueStart] ) {
- for ( j = 0; j < curShard->neighbours.Num(); j++ ) {
- nextShard = curShard->neighbours[j];
- if ( nextShard->droppedTime != -1 ) {
- continue;
- }
- if ( nextShard->islandNum ) {
- continue;
- }
- queue[queueEnd++] = nextShard;
- nextShard->islandNum = numIslands+1;
- if ( nextShard->atEdge ) {
- touchesEdge = true;
- }
- }
- }
- numIslands++;
- // if the island is not connected to the world at any edges
- if ( !touchesEdge ) {
- for ( j = 0; j < queueEnd; j++ ) {
- DropShard( queue[j], point, dir, 0.0f, time );
- }
- }
- }
- }
- /*
- ================
- idBrittleFracture::Break
- ================
- */
- void idBrittleFracture::Break( void ) {
- fl.takedamage = false;
- physicsObj.SetContents( CONTENTS_RENDERMODEL | CONTENTS_TRIGGER );
- }
- /*
- ================
- idBrittleFracture::IsBroken
- ================
- */
- bool idBrittleFracture::IsBroken( void ) const {
- return ( fl.takedamage == false );
- }
- /*
- ================
- idBrittleFracture::Killed
- ================
- */
- void idBrittleFracture::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
- if ( !disableFracture ) {
- ActivateTargets( this );
- Break();
- }
- }
- /*
- ================
- idBrittleFracture::AddDamageEffect
- ================
- */
- void idBrittleFracture::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
- if ( !disableFracture ) {
- ProjectDecal( collision.c.point, collision.c.normal, gameLocal.time, damageDefName );
- }
- }
- /*
- ================
- idBrittleFracture::Fracture_r
- ================
- */
- void idBrittleFracture::Fracture_r( idFixedWinding &w ) {
- int i, j, bestPlane;
- float a, c, s, dist, bestDist;
- idVec3 origin;
- idPlane windingPlane, splitPlanes[2];
- idMat3 axis, axistemp;
- idFixedWinding back;
- idTraceModel trm;
- idClipModel *clipModel;
- while( 1 ) {
- origin = w.GetCenter();
- w.GetPlane( windingPlane );
- if ( w.GetArea() < maxShardArea ) {
- break;
- }
- // randomly create a split plane
- axis[2] = windingPlane.Normal();
- #ifdef _D3XP
- if ( isXraySurface ) {
- a = idMath::TWO_PI / 2.f;
- }
- else {
- a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
- }
- #else
- a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
- #endif
- c = cos( a );
- s = -sin( a );
- axis[2].NormalVectors( axistemp[0], axistemp[1] );
- axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
- axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;
- // get the best split plane
- bestDist = 0.0f;
- bestPlane = 0;
- for ( i = 0; i < 2; i++ ) {
- splitPlanes[i].SetNormal( axis[i] );
- splitPlanes[i].FitThroughPoint( origin );
- for ( j = 0; j < w.GetNumPoints(); j++ ) {
- dist = splitPlanes[i].Distance( w[j].ToVec3() );
- if ( dist > bestDist ) {
- bestDist = dist;
- bestPlane = i;
- }
- }
- }
- // split the winding
- if ( !w.Split( &back, splitPlanes[bestPlane] ) ) {
- break;
- }
- // recursively create shards for the back winding
- Fracture_r( back );
- }
- // translate the winding to it's center
- origin = w.GetCenter();
- for ( j = 0; j < w.GetNumPoints(); j++ ) {
- w[j].ToVec3() -= origin;
- }
- w.RemoveEqualPoints();
- trm.SetupPolygon( w );
- trm.Shrink( CM_CLIP_EPSILON );
- clipModel = new idClipModel( trm );
- physicsObj.SetClipModel( clipModel, 1.0f, shards.Num() );
- physicsObj.SetOrigin( GetPhysics()->GetOrigin() + origin, shards.Num() );
- physicsObj.SetAxis( GetPhysics()->GetAxis(), shards.Num() );
- AddShard( clipModel, w );
- }
- /*
- ================
- idBrittleFracture::CreateFractures
- ================
- */
- void idBrittleFracture::CreateFractures( const idRenderModel *renderModel ) {
- int i, j, k;
- const modelSurface_t *surf;
- const idDrawVert *v;
- idFixedWinding w;
- if ( !renderModel ) {
- return;
- }
- physicsObj.SetSelf( this );
- physicsObj.SetOrigin( GetPhysics()->GetOrigin(), 0 );
- physicsObj.SetAxis( GetPhysics()->GetAxis(), 0 );
- #ifdef _D3XP
- if ( isXraySurface ) {
- for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
- surf = renderModel->Surface( i );
- material = surf->shader;
- w.Clear();
- int k = 0;
- v = &surf->geometry->verts[k];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- k = 1;
- v = &surf->geometry->verts[k];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- k = 3;
- v = &surf->geometry->verts[k];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- k = 2;
- v = &surf->geometry->verts[k];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- Fracture_r( w );
- }
- }
- else {
- for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
- surf = renderModel->Surface( i );
- material = surf->shader;
- for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
- w.Clear();
- for ( k = 0; k < 3; k++ ) {
- v = &surf->geometry->verts[ surf->geometry->indexes[ j + 2 - k ] ];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- }
- Fracture_r( w );
- }
- }
- }
- #else
- for ( i = 0; i < 1 /*renderModel->NumSurfaces()*/; i++ ) {
- surf = renderModel->Surface( i );
- material = surf->shader;
- for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
- w.Clear();
- for ( k = 0; k < 3; k++ ) {
- v = &surf->geometry->verts[ surf->geometry->indexes[ j + 2 - k ] ];
- w.AddPoint( v->xyz );
- w[k].s = v->st[0];
- w[k].t = v->st[1];
- }
- Fracture_r( w );
- }
- }
- #endif
- physicsObj.SetContents( material->GetContentFlags() );
- SetPhysics( &physicsObj );
- }
- /*
- ================
- idBrittleFracture::FindNeighbours
- ================
- */
- void idBrittleFracture::FindNeighbours( void ) {
- int i, j, k, l;
- idVec3 p1, p2, dir;
- idMat3 axis;
- idPlane plane[4];
- for ( i = 0; i < shards.Num(); i++ ) {
- shard_t *shard1 = shards[i];
- const idWinding &w1 = shard1->winding;
- const idVec3 &origin1 = shard1->clipModel->GetOrigin();
- const idMat3 &axis1 = shard1->clipModel->GetAxis();
- for ( k = 0; k < w1.GetNumPoints(); k++ ) {
- p1 = origin1 + w1[k].ToVec3() * axis1;
- p2 = origin1 + w1[(k+1)%w1.GetNumPoints()].ToVec3() * axis1;
- dir = p2 - p1;
- dir.Normalize();
- axis = dir.ToMat3();
- plane[0].SetNormal( dir );
- plane[0].FitThroughPoint( p1 );
- plane[1].SetNormal( -dir );
- plane[1].FitThroughPoint( p2 );
- plane[2].SetNormal( axis[1] );
- plane[2].FitThroughPoint( p1 );
- plane[3].SetNormal( axis[2] );
- plane[3].FitThroughPoint( p1 );
- for ( j = 0; j < shards.Num(); j++ ) {
- if ( i == j ) {
- continue;
- }
- shard_t *shard2 = shards[j];
- for ( l = 0; l < shard1->neighbours.Num(); l++ ) {
- if ( shard1->neighbours[l] == shard2 ) {
- break;
- }
- }
- if ( l < shard1->neighbours.Num() ) {
- continue;
- }
- const idWinding &w2 = shard2->winding;
- const idVec3 &origin2 = shard2->clipModel->GetOrigin();
- const idMat3 &axis2 = shard2->clipModel->GetAxis();
- for ( l = w2.GetNumPoints()-1; l >= 0; l-- ) {
- p1 = origin2 + w2[l].ToVec3() * axis2;
- p2 = origin2 + w2[(l-1+w2.GetNumPoints())%w2.GetNumPoints()].ToVec3() * axis2;
- if ( plane[0].Side( p2, 0.1f ) == SIDE_FRONT && plane[1].Side( p1, 0.1f ) == SIDE_FRONT ) {
- if ( plane[2].Side( p1, 0.1f ) == SIDE_ON && plane[3].Side( p1, 0.1f ) == SIDE_ON ) {
- if ( plane[2].Side( p2, 0.1f ) == SIDE_ON && plane[3].Side( p2, 0.1f ) == SIDE_ON ) {
- shard1->neighbours.Append( shard2 );
- shard1->edgeHasNeighbour[k] = true;
- shard2->neighbours.Append( shard1 );
- shard2->edgeHasNeighbour[(l-1+w2.GetNumPoints())%w2.GetNumPoints()] = true;
- break;
- }
- }
- }
- }
- }
- }
- for ( k = 0; k < w1.GetNumPoints(); k++ ) {
- if ( !shard1->edgeHasNeighbour[k] ) {
- break;
- }
- }
- if ( k < w1.GetNumPoints() ) {
- shard1->atEdge = true;
- } else {
- shard1->atEdge = false;
- }
- }
- }
- /*
- ================
- idBrittleFracture::Event_Activate
- ================
- */
- void idBrittleFracture::Event_Activate( idEntity *activator ) {
- disableFracture = false;
- if ( health <= 0 ) {
- Break();
- }
- }
- /*
- ================
- idBrittleFracture::Event_Touch
- ================
- */
- void idBrittleFracture::Event_Touch( idEntity *other, trace_t *trace ) {
- idVec3 point, impulse;
- if ( !IsBroken() ) {
- return;
- }
- if ( trace->c.id < 0 || trace->c.id >= shards.Num() ) {
- return;
- }
- point = shards[trace->c.id]->clipModel->GetOrigin();
- impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass();
- Shatter( point, impulse, gameLocal.time );
- }
- /*
- ================
- idBrittleFracture::ClientPredictionThink
- ================
- */
- void idBrittleFracture::ClientPredictionThink( void ) {
- // only think forward because the state is not synced through snapshots
- if ( !gameLocal.isNewFrame ) {
- return;
- }
- Think();
- }
- /*
- ================
- idBrittleFracture::ClientReceiveEvent
- ================
- */
- bool idBrittleFracture::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
- idVec3 point, dir;
- switch( event ) {
- case EVENT_PROJECT_DECAL: {
- point[0] = msg.ReadFloat();
- point[1] = msg.ReadFloat();
- point[2] = msg.ReadFloat();
- dir[0] = msg.ReadFloat();
- dir[1] = msg.ReadFloat();
- dir[2] = msg.ReadFloat();
- ProjectDecal( point, dir, time, NULL );
- return true;
- }
- case EVENT_SHATTER: {
- point[0] = msg.ReadFloat();
- point[1] = msg.ReadFloat();
- point[2] = msg.ReadFloat();
- dir[0] = msg.ReadFloat();
- dir[1] = msg.ReadFloat();
- dir[2] = msg.ReadFloat();
- Shatter( point, dir, time );
- return true;
- }
- default: {
- return idEntity::ClientReceiveEvent( event, time, msg );
- }
- }
- return false;
- }
|