doom3-bfg/neo/d3xp/BrittleFracture.cpp

1556 lines
37 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
2012-11-26 18:58:24 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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 "precompiled.h"
2012-11-26 18:58:24 +00:00
#pragma hdrstop
#include "Game_local.h"
CLASS_DECLARATION( idEntity, idBrittleFracture )
EVENT( EV_Activate, idBrittleFracture::Event_Activate )
EVENT( EV_Touch, idBrittleFracture::Event_Touch )
2012-11-26 18:58:24 +00:00
END_CLASS
const int SHARD_ALIVE_TIME = 5000;
const int SHARD_FADE_START = 2000;
static const char* brittleFracture_SnapshotName = "_BrittleFracture_Snapshot_";
2012-11-26 18:58:24 +00:00
/*
================
idBrittleFracture::idBrittleFracture
================
*/
idBrittleFracture::idBrittleFracture()
{
2012-11-26 18:58:24 +00:00
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();
2012-11-26 18:58:24 +00:00
bounds.Clear();
disableFracture = false;
2012-11-26 18:58:24 +00:00
lastRenderEntityUpdate = -1;
changed = false;
2012-11-26 18:58:24 +00:00
fl.networkSync = true;
2012-11-26 18:58:24 +00:00
isXraySurface = false;
}
/*
================
idBrittleFracture::~idBrittleFracture
================
*/
idBrittleFracture::~idBrittleFracture()
{
2012-11-26 18:58:24 +00:00
int i;
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
shards[i]->decals.DeleteContents( true );
delete shards[i];
}
2012-11-26 18:58:24 +00:00
// make sure the render entity is freed before the model is freed
FreeModelDef();
renderModelManager->FreeModel( renderEntity.hModel );
2012-11-26 18:58:24 +00:00
// Free our events list memory
storedEvents.Clear();
}
/*
================
idBrittleFracture::Save
================
*/
void idBrittleFracture::Save( idSaveGame* savefile ) const
{
2012-11-26 18:58:24 +00:00
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 );
2012-11-26 18:58:24 +00:00
// state
savefile->WriteBounds( bounds );
savefile->WriteBool( disableFracture );
2012-11-26 18:58:24 +00:00
savefile->WriteInt( lastRenderEntityUpdate );
savefile->WriteBool( changed );
2012-11-26 18:58:24 +00:00
savefile->WriteModel( defaultRenderModel );
2012-11-26 18:58:24 +00:00
// So we can re-break the object on load if needed
savefile->WriteInt( storedEvents.Num() );
for( int i = 0; i < storedEvents.Num(); ++i )
{
2012-11-26 18:58:24 +00:00
savefile->WriteInt( storedEvents[i].eventType );
savefile->WriteVec3( storedEvents[i].point );
savefile->WriteVec3( storedEvents[i].vector );
}
savefile->WriteBool( isXraySurface );
}
/*
================
idBrittleFracture::Restore
================
*/
void idBrittleFracture::Restore( idRestoreGame* savefile )
{
2012-11-26 18:58:24 +00:00
savefile->ReadInt( health );
savefile->Read( &fl, sizeof( fl ) );
LittleBitField( &fl, sizeof( fl ) );
2012-11-26 18:58:24 +00:00
// 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 );
2012-11-26 18:58:24 +00:00
// state
savefile->ReadBounds( bounds );
2012-11-26 18:58:24 +00:00
savefile->ReadBool( disableFracture );
2012-11-26 18:58:24 +00:00
savefile->ReadInt( lastRenderEntityUpdate );
savefile->ReadBool( changed );
2012-11-26 18:58:24 +00:00
savefile->ReadModel( defaultRenderModel );
2012-11-26 18:58:24 +00:00
// Reset all brittle Fractures so we can re-break them if necessary
fl.takedamage = true;
CreateFractures( defaultRenderModel );
FindNeighbours();
2012-11-26 18:58:24 +00:00
int numEvents = 0;
bool resolveBreaks = false;
savefile->ReadInt( numEvents );
for( int i = 0; i < numEvents; i++ )
{
2012-11-26 18:58:24 +00:00
fractureEvent_s restoredEvent;
2012-11-26 18:58:24 +00:00
savefile->ReadInt( restoredEvent.eventType );
savefile->ReadVec3( restoredEvent.point );
savefile->ReadVec3( restoredEvent.vector );
if( restoredEvent.eventType == EVENT_PROJECT_DECAL )
{
2012-11-26 18:58:24 +00:00
ProjectDecal( restoredEvent.point, restoredEvent.vector, gameLocal.time, NULL );
}
else
{
2012-11-26 18:58:24 +00:00
Shatter( restoredEvent.point, restoredEvent.vector, gameLocal.time );
}
resolveBreaks = true;
}
2012-11-26 18:58:24 +00:00
// remove any dropped shards
for( int i = 0; resolveBreaks && i < shards.Num(); i++ )
{
if( shards[i]->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
RemoveShard( i );
i--;
}
}
2012-11-26 18:58:24 +00:00
renderEntity.hModel = renderModelManager->AllocModel();
renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName );
renderEntity.callback = idBrittleFracture::ModelCallback;
renderEntity.noShadow = true;
renderEntity.noSelfShadow = true;
renderEntity.noDynamicInteractions = false;
2012-11-26 18:58:24 +00:00
savefile->ReadBool( isXraySurface );
}
/*
================
idBrittleFracture::Spawn
================
*/
void idBrittleFracture::Spawn()
{
2012-11-26 18:58:24 +00:00
// get shard properties
decalMaterial = declManager->FindMaterial( spawnArgs.GetString( "mtr_decal" ) );
decalSize = spawnArgs.GetFloat( "decalSize", "40" );
maxShardArea = spawnArgs.GetFloat( "maxShardArea", "200" ) * 2.0f ;
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" );
2012-11-26 18:58:24 +00:00
// make sure that max is greater than min ( otherwise negative number square root happens )
if( maxShatterRadius < minShatterRadius )
{
2012-11-26 18:58:24 +00:00
idLib::Warning( "BrittleFracture, minShatterRadius(%2f) is greater than maxShatterRadius(%2f). Unknown results will ensue.", minShatterRadius, maxShatterRadius );
}
2012-11-26 18:58:24 +00:00
// 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 );
2012-11-26 18:58:24 +00:00
disableFracture = spawnArgs.GetBool( "disableFracture", "0" );
health = spawnArgs.GetInt( "health", "40" );
fl.takedamage = true;
2012-11-26 18:58:24 +00:00
// FIXME: set "bleed" so idProjectile calls AddDamageEffect
spawnArgs.SetBool( "bleed", 1 );
2012-11-26 18:58:24 +00:00
// check for xray surface
if( renderEntity.hModel != NULL )
{
const idRenderModel* model = renderEntity.hModel;
2012-11-26 18:58:24 +00:00
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" )
{
2012-11-26 18:58:24 +00:00
isXraySurface = true;
break;
}
}
}
2012-11-26 18:58:24 +00:00
CreateFractures( renderEntity.hModel );
2012-11-26 18:58:24 +00:00
FindNeighbours();
2012-11-26 18:58:24 +00:00
defaultRenderModel = renderEntity.hModel;
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( TAG_PARTICLE ) shard_t;
2012-11-26 18:58:24 +00:00
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 )
{
2012-11-26 18:58:24 +00:00
int i;
2012-11-26 18:58:24 +00:00
delete shards[index];
shards.RemoveIndex( index );
physicsObj.RemoveIndex( index );
for( i = index; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
shards[i]->clipModel->SetId( i );
}
}
/*
================
idBrittleFracture::UpdateRenderEntity
================
*/
bool idBrittleFracture::UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView ) const
{
2012-11-26 18:58:24 +00:00
int i, j, k, n, msec, numTris, numDecalTris;
float fade;
dword packedColor;
srfTriangles_t* tris, *decalTris;
2012-11-26 18:58:24 +00:00
modelSurface_t surface;
idDrawVert* v;
2012-11-26 18:58:24 +00:00
idPlane plane;
idMat3 tangents;
2012-11-26 18:58:24 +00:00
// 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 )
{
2012-11-26 18:58:24 +00:00
return false;
}
2012-11-26 18:58:24 +00:00
// don't regenerate it if it is current
if( lastRenderEntityUpdate == gameLocal.time || !changed )
{
2012-11-26 18:58:24 +00:00
return false;
}
2012-11-26 18:58:24 +00:00
lastRenderEntityUpdate = gameLocal.time;
changed = false;
2012-11-26 18:58:24 +00:00
numTris = 0;
numDecalTris = 0;
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
n = shards[i]->winding.GetNumPoints();
if( n > 2 )
{
2012-11-26 18:58:24 +00:00
numTris += n - 2;
}
for( k = 0; k < shards[i]->decals.Num(); k++ )
{
2012-11-26 18:58:24 +00:00
n = shards[i]->decals[k]->GetNumPoints();
if( n > 2 )
{
2012-11-26 18:58:24 +00:00
numDecalTris += n - 2;
}
}
}
2012-11-26 18:58:24 +00:00
// FIXME: re-use model surfaces
renderEntity->hModel->InitEmpty( brittleFracture_SnapshotName );
2012-11-26 18:58:24 +00:00
// 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();
2012-11-26 18:58:24 +00:00
fade = 1.0f;
if( shards[i]->droppedTime >= 0 )
{
2012-11-26 18:58:24 +00:00
msec = gameLocal.time - shards[i]->droppedTime - SHARD_FADE_START;
if( msec > 0 )
{
fade = 1.0f - ( float ) msec / ( SHARD_ALIVE_TIME - SHARD_FADE_START );
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
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;
2012-11-26 18:58:24 +00:00
winding.GetPlane( plane );
tangents = ( plane.Normal() * axis ).ToMat3();
for( j = 2; j < winding.GetNumPoints(); j++ )
{
2012-11-26 18:58:24 +00:00
v = &tris->verts[tris->numVerts++];
v->Clear();
v->xyz = origin + winding[0].ToVec3() * axis;
v->SetTexCoord( winding[0].s, winding[0].t );
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
v = &tris->verts[tris->numVerts++];
v->Clear();
v->xyz = origin + winding[j - 1].ToVec3() * axis;
v->SetTexCoord( winding[j - 1].s, winding[j - 1].t );
2012-11-26 18:58:24 +00:00
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
v = &tris->verts[tris->numVerts++];
v->Clear();
v->xyz = origin + winding[j].ToVec3() * axis;
v->SetTexCoord( winding[j].s, winding[j].t );
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
if( material->ShouldCreateBackSides() )
{
2012-11-26 18:58:24 +00:00
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++ )
{
2012-11-26 18:58:24 +00:00
v = &decalTris->verts[decalTris->numVerts++];
v->Clear();
v->xyz = origin + decalWinding[0].ToVec3() * axis;
v->SetTexCoord( decalWinding[0].s, decalWinding[0].t );
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
v = &decalTris->verts[decalTris->numVerts++];
v->Clear();
v->xyz = origin + decalWinding[j - 1].ToVec3() * axis;
v->SetTexCoord( decalWinding[j - 1].s, decalWinding[j - 1].t );
2012-11-26 18:58:24 +00:00
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
v = &decalTris->verts[decalTris->numVerts++];
v->Clear();
v->xyz = origin + decalWinding[j].ToVec3() * axis;
v->SetTexCoord( decalWinding[j].s, decalWinding[j].t );
v->SetNormal( tangents[0] );
v->SetTangent( tangents[1] );
v->SetBiTangent( tangents[2] );
v->SetColor( packedColor );
2012-11-26 18:58:24 +00:00
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
if( decalMaterial->ShouldCreateBackSides() )
{
2012-11-26 18:58:24 +00:00
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 2;
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 3;
decalTris->indexes[decalTris->numIndexes++] = decalTris->numVerts - 1;
}
}
}
}
2012-11-26 18:58:24 +00:00
tris->tangentsCalculated = true;
decalTris->tangentsCalculated = true;
2012-11-26 18:58:24 +00:00
SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts );
SIMDProcessor->MinMax( decalTris->bounds[0], decalTris->bounds[1], decalTris->verts, decalTris->numVerts );
2012-11-26 18:58:24 +00:00
memset( &surface, 0, sizeof( surface ) );
surface.shader = material;
surface.id = 0;
surface.geometry = tris;
renderEntity->hModel->AddSurface( surface );
2012-11-26 18:58:24 +00:00
memset( &surface, 0, sizeof( surface ) );
surface.shader = decalMaterial;
surface.id = 1;
surface.geometry = decalTris;
renderEntity->hModel->AddSurface( surface );
2012-11-26 18:58:24 +00:00
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 == NULL )
{
2012-11-26 18:58:24 +00:00
gameLocal.Error( "idBrittleFracture::ModelCallback: callback with NULL game entity" );
return false;
}
2012-11-26 18:58:24 +00:00
return ent->UpdateRenderEntity( renderEntity, renderView );
}
/*
================
idBrittleFracture::Present
================
*/
void idBrittleFracture::Present()
{
2012-11-26 18:58:24 +00:00
// don't present to the renderer if the entity hasn't changed
if( !( thinkFlags & TH_UPDATEVISUALS ) )
{
2012-11-26 18:58:24 +00:00
return;
}
BecomeInactive( TH_UPDATEVISUALS );
2012-11-26 18:58:24 +00:00
renderEntity.bounds = bounds;
renderEntity.origin.Zero();
renderEntity.axis.Identity();
2012-11-26 18:58:24 +00:00
// force an update because the bounds/origin/axis may stay the same while the model changes
renderEntity.forceUpdate = true;
2012-11-26 18:58:24 +00:00
// add to refresh list
if( modelDefHandle == -1 )
{
2012-11-26 18:58:24 +00:00
modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
}
else
{
2012-11-26 18:58:24 +00:00
gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
}
2012-11-26 18:58:24 +00:00
changed = true;
}
/*
================
idBrittleFracture::Think
================
*/
void idBrittleFracture::Think()
{
2012-11-26 18:58:24 +00:00
int i, startTime, endTime, droppedTime;
shard_t* shard;
2012-11-26 18:58:24 +00:00
bool atRest = true, fading = false;
2012-11-26 18:58:24 +00:00
// remove overdue shards
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
droppedTime = shards[i]->droppedTime;
if( droppedTime != -1 )
{
if( gameLocal.time - droppedTime > SHARD_ALIVE_TIME )
{
2012-11-26 18:58:24 +00:00
RemoveShard( i );
i--;
}
fading = true;
}
}
2012-11-26 18:58:24 +00:00
// remove the entity when nothing is visible
if( !shards.Num() )
{
2012-11-26 18:58:24 +00:00
PostEventMS( &EV_Remove, 0 );
return;
}
if( thinkFlags & TH_PHYSICS )
{
2012-11-26 18:58:24 +00:00
startTime = gameLocal.previousTime;
endTime = gameLocal.time;
2012-11-26 18:58:24 +00:00
// run physics on shards
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
shard = shards[i];
if( shard->droppedTime == -1 )
{
2012-11-26 18:58:24 +00:00
continue;
}
2012-11-26 18:58:24 +00:00
shard->physicsObj.Evaluate( endTime - startTime, endTime );
if( !shard->physicsObj.IsAtRest() )
{
2012-11-26 18:58:24 +00:00
atRest = false;
}
}
if( atRest )
{
2012-11-26 18:58:24 +00:00
BecomeInactive( TH_PHYSICS );
}
else
{
2012-11-26 18:58:24 +00:00
BecomeActive( TH_PHYSICS );
}
}
if( !atRest || bounds.IsCleared() )
{
2012-11-26 18:58:24 +00:00
bounds.Clear();
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
bounds.AddBounds( shards[i]->clipModel->GetAbsBounds() );
}
}
if( fading )
{
2012-11-26 18:58:24 +00:00
BecomeActive( TH_UPDATEVISUALS | TH_THINK );
}
else
{
2012-11-26 18:58:24 +00:00
BecomeInactive( TH_THINK );
}
2012-11-26 18:58:24 +00:00
RunPhysics();
Present();
}
/*
================
idBrittleFracture::ApplyImpulse
================
*/
void idBrittleFracture::ApplyImpulse( idEntity* ent, int id, const idVec3& point, const idVec3& impulse )
{
2012-11-26 18:58:24 +00:00
if( id < 0 || id >= shards.Num() )
{
2012-11-26 18:58:24 +00:00
return;
}
if( shards[id]->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
shards[id]->physicsObj.ApplyImpulse( 0, point, impulse );
}
else if( health <= 0 && !disableFracture )
{
2012-11-26 18:58:24 +00:00
Shatter( point, impulse, gameLocal.time );
}
}
/*
================
idBrittleFracture::AddForce
================
*/
void idBrittleFracture::AddForce( idEntity* ent, int id, const idVec3& point, const idVec3& force )
{
2012-11-26 18:58:24 +00:00
if( id < 0 || id >= shards.Num() )
{
2012-11-26 18:58:24 +00:00
return;
}
if( shards[id]->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
shards[id]->physicsObj.AddForce( 0, point, force );
}
else if( health <= 0 && !disableFracture )
{
2012-11-26 18:58:24 +00:00
Shatter( point, force, gameLocal.time );
}
}
/*
================
idBrittleFracture::ProjectDecal
================
*/
void idBrittleFracture::ProjectDecal( const idVec3& point, const idVec3& dir, const int time, const char* damageDefName )
{
2012-11-26 18:58:24 +00:00
int i, j, bits, clipBits;
float a, c, s;
idVec2 st[MAX_POINTS_ON_WINDING];
idVec3 origin;
idMat3 axis, axistemp;
idPlane textureAxis[2];
if( common->IsServer() )
{
2012-11-26 18:58:24 +00:00
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
2012-11-26 18:58:24 +00:00
msg.InitWrite( 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 );
}
2012-11-26 18:58:24 +00:00
// store the event so we can rebuilt the fracture after loading a save
fractureEvent_s fractureEvent;
fractureEvent.eventType = EVENT_PROJECT_DECAL;
fractureEvent.point = point;
fractureEvent.vector = dir;
storedEvents.Append( fractureEvent );
if( time >= gameLocal.time )
{
2012-11-26 18:58:24 +00:00
// try to get the sound from the damage def
const idDeclEntityDef* damageDef = NULL;
const idSoundShader* sndShader = NULL;
if( damageDefName )
{
2012-11-26 18:58:24 +00:00
damageDef = gameLocal.FindEntityDef( damageDefName, false );
if( damageDef )
{
const char* sndName = damageDef->dict.GetString( "snd_shatter", "" );
if( sndName[0] != 0 )
{
2012-11-26 18:58:24 +00:00
sndShader = declManager->FindSound( sndName );
}
}
}
if( sndShader )
{
2012-11-26 18:58:24 +00:00
StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL );
}
else
{
2012-11-26 18:58:24 +00:00
StartSound( "snd_bullethole", SND_CHANNEL_ANY, 0, false, NULL );
}
}
2012-11-26 18:58:24 +00:00
a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
c = cos( a );
s = -sin( a );
2012-11-26 18:58:24 +00:00
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;
2012-11-26 18:58:24 +00:00
textureAxis[0] = axis[0] * ( 1.0f / decalSize );
textureAxis[0][3] = -( point * textureAxis[0].Normal() ) + 0.5f;
2012-11-26 18:58:24 +00:00
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;
2012-11-26 18:58:24 +00:00
origin = shards[i]->clipModel->GetOrigin();
axis = shards[i]->clipModel->GetAxis();
float d0, d1;
2012-11-26 18:58:24 +00:00
clipBits = -1;
for( j = 0; j < winding.GetNumPoints(); j++ )
{
2012-11-26 18:58:24 +00:00
idVec3 p = origin + winding[j].ToVec3() * axis;
2012-11-26 18:58:24 +00:00
st[j].x = d0 = textureAxis[0].Distance( p );
st[j].y = d1 = textureAxis[1].Distance( p );
2012-11-26 18:58:24 +00:00
bits = IEEE_FLT_SIGNBITSET( d0 );
d0 = 1.0f - d0;
bits |= IEEE_FLT_SIGNBITSET( d1 ) << 2;
d1 = 1.0f - d1;
bits |= IEEE_FLT_SIGNBITSET( d0 ) << 1;
bits |= IEEE_FLT_SIGNBITSET( d1 ) << 3;
2012-11-26 18:58:24 +00:00
clipBits &= bits;
}
if( clipBits )
{
2012-11-26 18:58:24 +00:00
continue;
}
idFixedWinding* decal = new( TAG_PARTICLE ) idFixedWinding;
2012-11-26 18:58:24 +00:00
shards[i]->decals.Append( decal );
2012-11-26 18:58:24 +00:00
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;
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
BecomeActive( TH_UPDATEVISUALS );
}
/*
================
idBrittleFracture::DropShard
================
*/
void idBrittleFracture::DropShard( shard_t* shard, const idVec3& point, const idVec3& dir, const float impulse, const int time )
{
2012-11-26 18:58:24 +00:00
int i, j, clipModelId;
float dist, f;
idVec3 dir2, origin;
idMat3 axis;
shard_t* neighbour;
2012-11-26 18:58:24 +00:00
// don't display decals on dropped shards
shard->decals.DeleteContents( true );
2012-11-26 18:58:24 +00:00
// remove neighbour pointers of neighbours pointing to this shard
for( i = 0; i < shard->neighbours.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
neighbour = shard->neighbours[i];
for( j = 0; j < neighbour->neighbours.Num(); j++ )
{
if( neighbour->neighbours[j] == shard )
{
2012-11-26 18:58:24 +00:00
neighbour->neighbours.RemoveIndex( j );
break;
}
}
}
2012-11-26 18:58:24 +00:00
// remove neighbour pointers
shard->neighbours.Clear();
2012-11-26 18:58:24 +00:00
// remove the clip model from the static physics object
clipModelId = shard->clipModel->GetId();
physicsObj.SetClipModel( NULL, 1.0f, clipModelId, false );
2012-11-26 18:58:24 +00:00
origin = shard->clipModel->GetOrigin();
axis = shard->clipModel->GetAxis();
2012-11-26 18:58:24 +00:00
// set the dropped time for fading
shard->droppedTime = time;
2012-11-26 18:58:24 +00:00
dir2 = origin - point;
dist = dir2.Normalize();
f = dist > maxShatterRadius ? 1.0f : idMath::Sqrt( idMath::Fabs( dist - minShatterRadius ) ) * ( 1.0f / idMath::Sqrt( idMath::Fabs( maxShatterRadius - minShatterRadius ) ) );
2012-11-26 18:58:24 +00:00
// 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 ) );
2012-11-26 18:58:24 +00:00
shard->clipModel->SetId( clipModelId );
2012-11-26 18:58:24 +00:00
BecomeActive( TH_PHYSICS );
}
/*
================
idBrittleFracture::Shatter
================
*/
void idBrittleFracture::Shatter( const idVec3& point, const idVec3& impulse, const int time )
{
2012-11-26 18:58:24 +00:00
int i;
idVec3 dir;
shard_t* shard;
2012-11-26 18:58:24 +00:00
float m;
if( common->IsServer() )
{
2012-11-26 18:58:24 +00:00
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
2012-11-26 18:58:24 +00:00
msg.InitWrite( 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 );
}
2012-11-26 18:58:24 +00:00
// Store off the event so we can rebuilt the object if we reload a savegame
fractureEvent_s fractureEvent;
fractureEvent.eventType = EVENT_SHATTER;
fractureEvent.point = point;
fractureEvent.vector = impulse;
storedEvents.Append( fractureEvent );
if( time > ( gameLocal.time - SHARD_ALIVE_TIME ) )
{
2012-11-26 18:58:24 +00:00
StartSound( "snd_shatter", SND_CHANNEL_ANY, 0, false, NULL );
}
if( !IsBroken() )
{
2012-11-26 18:58:24 +00:00
Break();
}
if( fxFracture.Length() )
{
2012-11-26 18:58:24 +00:00
idEntityFx::StartFx( fxFracture, &point, &GetPhysics()->GetAxis(), this, true );
}
2012-11-26 18:58:24 +00:00
dir = impulse;
m = dir.Normalize();
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
shard = shards[i];
if( shard->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( ( shard->clipModel->GetOrigin() - point ).LengthSqr() > Square( maxShatterRadius ) )
{
2012-11-26 18:58:24 +00:00
continue;
}
2012-11-26 18:58:24 +00:00
DropShard( shard, point, dir, m, time );
}
2012-11-26 18:58:24 +00:00
DropFloatingIslands( point, impulse, time );
}
/*
================
idBrittleFracture::DropFloatingIslands
================
*/
void idBrittleFracture::DropFloatingIslands( const idVec3& point, const idVec3& impulse, const int time )
{
2012-11-26 18:58:24 +00:00
int i, j, numIslands;
int queueStart, queueEnd;
shard_t* curShard, *nextShard, **queue;
2012-11-26 18:58:24 +00:00
bool touchesEdge;
idVec3 dir;
2012-11-26 18:58:24 +00:00
dir = impulse;
dir.Normalize();
2012-11-26 18:58:24 +00:00
numIslands = 0;
queue = ( shard_t** ) _alloca16( shards.Num() * sizeof( shard_t** ) );
for( i = 0; i < shards.Num(); i++ )
{
2012-11-26 18:58:24 +00:00
shards[i]->islandNum = 0;
}
for( i = 0; i < shards.Num(); i++ )
{
if( shards[i]->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( shards[i]->islandNum )
{
2012-11-26 18:58:24 +00:00
continue;
}
queueStart = 0;
2012-11-26 18:58:24 +00:00
queueEnd = 1;
queue[0] = shards[i];
shards[i]->islandNum = numIslands + 1;
2012-11-26 18:58:24 +00:00
touchesEdge = false;
if( shards[i]->atEdge )
{
2012-11-26 18:58:24 +00:00
touchesEdge = true;
}
for( curShard = queue[queueStart]; queueStart < queueEnd; curShard = queue[++queueStart] )
{
for( j = 0; j < curShard->neighbours.Num(); j++ )
{
2012-11-26 18:58:24 +00:00
nextShard = curShard->neighbours[j];
if( nextShard->droppedTime != -1 )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( nextShard->islandNum )
{
2012-11-26 18:58:24 +00:00
continue;
}
2012-11-26 18:58:24 +00:00
queue[queueEnd++] = nextShard;
nextShard->islandNum = numIslands + 1;
if( nextShard->atEdge )
{
2012-11-26 18:58:24 +00:00
touchesEdge = true;
}
}
}
numIslands++;
2012-11-26 18:58:24 +00:00
// if the island is not connected to the world at any edges
if( !touchesEdge )
{
for( j = 0; j < queueEnd; j++ )
{
2012-11-26 18:58:24 +00:00
DropShard( queue[j], point, dir, 0.0f, time );
}
}
}
}
/*
================
idBrittleFracture::Break
================
*/
void idBrittleFracture::Break()
{
2012-11-26 18:58:24 +00:00
fl.takedamage = false;
physicsObj.SetContents( CONTENTS_RENDERMODEL | CONTENTS_TRIGGER );
}
/*
================
idBrittleFracture::IsBroken
================
*/
bool idBrittleFracture::IsBroken() const
{
2012-11-26 18:58:24 +00:00
return ( fl.takedamage == false );
}
/*
================
idBrittleFracture::Killed
================
*/
void idBrittleFracture::Killed( idEntity* inflictor, idEntity* attacker, int damage, const idVec3& dir, int location )
{
if( !disableFracture )
{
2012-11-26 18:58:24 +00:00
ActivateTargets( this );
Break();
}
}
/*
================
idBrittleFracture::AddDamageEffect
================
*/
void idBrittleFracture::AddDamageEffect( const trace_t& collision, const idVec3& velocity, const char* damageDefName )
{
if( !disableFracture )
{
2012-11-26 18:58:24 +00:00
ProjectDecal( collision.c.point, collision.c.normal, gameLocal.time, damageDefName );
}
}
static float fractureSplitTable[] = { 1365.123f, 5.324f, 1125.34f, 50.34f, 555.252f, 100.12f, 230.53f, 10000.87f, 10000.87f };
/*
================
idBrittleFracture::Fracture_r
================
*/
void idBrittleFracture::Fracture_r( idFixedWinding& w, idRandom2& random )
{
2012-11-26 18:58:24 +00:00
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 )
{
2012-11-26 18:58:24 +00:00
origin = w.GetCenter();
w.GetPlane( windingPlane );
if( w.GetArea() < maxShardArea )
{
2012-11-26 18:58:24 +00:00
break;
}
2012-11-26 18:58:24 +00:00
// randomly create a split plane
axis[2] = windingPlane.Normal();
if( isXraySurface )
{
2012-11-26 18:58:24 +00:00
a = idMath::TWO_PI / 2.f;
}
else
{
2012-11-26 18:58:24 +00:00
a = random.RandomFloat() * idMath::TWO_PI;
}
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;
2012-11-26 18:58:24 +00:00
// get the best split plane
bestDist = 0.0f;
bestPlane = 0;
for( i = 0; i < 2; i++ )
{
2012-11-26 18:58:24 +00:00
splitPlanes[i].SetNormal( axis[i] );
splitPlanes[i].FitThroughPoint( origin );
for( j = 0; j < w.GetNumPoints(); j++ )
{
2012-11-26 18:58:24 +00:00
dist = splitPlanes[i].Distance( w[j].ToVec3() );
if( dist > bestDist )
{
2012-11-26 18:58:24 +00:00
bestDist = dist;
bestPlane = i;
}
}
}
2012-11-26 18:58:24 +00:00
// split the winding
if( !w.Split( &back, splitPlanes[bestPlane] ) )
{
2012-11-26 18:58:24 +00:00
break;
}
2012-11-26 18:58:24 +00:00
// recursively create shards for the back winding
Fracture_r( back, random );
}
2012-11-26 18:58:24 +00:00
// translate the winding to it's center
origin = w.GetCenter();
for( j = 0; j < w.GetNumPoints(); j++ )
{
2012-11-26 18:58:24 +00:00
w[j].ToVec3() -= origin;
}
w.RemoveEqualPoints();
2012-11-26 18:58:24 +00:00
trm.SetupPolygon( w );
trm.Shrink( CM_CLIP_EPSILON );
clipModel = new( TAG_PHYSICS ) idClipModel( trm, false );
2012-11-26 18:58:24 +00:00
physicsObj.SetClipModel( clipModel, 1.0f, shards.Num() );
physicsObj.SetOrigin( GetPhysics()->GetOrigin() + origin, shards.Num() );
physicsObj.SetAxis( GetPhysics()->GetAxis(), shards.Num() );
2012-11-26 18:58:24 +00:00
AddShard( clipModel, w );
}
/*
================
CompareVec5
================
*/
bool CompareVec5( const idVec5& v0, const idVec5& v1 )
{
2012-11-26 18:58:24 +00:00
float dx = v0.x - v1.x;
float dy = v0.y - v1.y;
float dz = v0.z - v1.z;
float ds = v0.s - v1.s;
float dt = v0.t - v1.t;
float d = ( dx * dx ) + ( dy * dy ) + ( dz * dz ) + ( ds * ds ) + ( dt + dt );
return ( d == 0.0f );
}
/*
================
idBrittleFracture::CreateFractures
================
*/
void idBrittleFracture::CreateFractures( const idRenderModel* renderModel )
{
if( !renderModel || renderModel->NumSurfaces() < 1 )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
physicsObj.SetSelf( this );
physicsObj.SetOrigin( GetPhysics()->GetOrigin(), 0 );
physicsObj.SetAxis( GetPhysics()->GetAxis(), 0 );
const modelSurface_t* surf = renderModel->Surface( 0 );
2012-11-26 18:58:24 +00:00
material = surf->shader;
2012-11-26 18:58:24 +00:00
idMat3 physAxis;
physAxis = physicsObj.GetAxis();
if( isXraySurface )
{
2012-11-26 18:58:24 +00:00
idFixedWinding w;
for( int i = 0; i < 4; i++ )
{
const idDrawVert* v = &surf->geometry->verts[i];
2012-11-26 18:58:24 +00:00
w.AddPoint( idVec5( v->xyz, v->GetTexCoord() ) );
}
2012-11-26 18:58:24 +00:00
idRandom2 random( entityNumber );
Fracture_r( w , random );
}
else
2012-11-26 18:58:24 +00:00
{
const idDrawVert* verts = surf->geometry->verts;
triIndex_t* indexes = surf->geometry->indexes;
for( int j = 0; j < surf->geometry->numIndexes; j += 3 )
{
2012-11-26 18:58:24 +00:00
int i0 = indexes[ j + 0 ];
int i1 = indexes[ j + 1 ];
int i2 = indexes[ j + 2 ];
idFixedWinding w;
w.AddPoint( idVec5( verts[i2].xyz, verts[i2].GetTexCoord() ) );
w.AddPoint( idVec5( verts[i1].xyz, verts[i1].GetTexCoord() ) );
w.AddPoint( idVec5( verts[i0].xyz, verts[i0].GetTexCoord() ) );
idPlane p1;
w.GetPlane( p1 );
for( int k = j + 3; k < surf->geometry->numIndexes && ( w.GetNumPoints() + 1 < MAX_POINTS_ON_WINDING ); k += 3 )
{
2012-11-26 18:58:24 +00:00
int i3 = indexes[ k + 0 ];
int i4 = indexes[ k + 1 ];
int i5 = indexes[ k + 2 ];
idFixedWinding w2;
w2.AddPoint( idVec5( verts[i5].xyz, verts[i5].GetTexCoord() ) );
w2.AddPoint( idVec5( verts[i4].xyz, verts[i4].GetTexCoord() ) );
w2.AddPoint( idVec5( verts[i3].xyz, verts[i3].GetTexCoord() ) );
idPlane p2;
w2.GetPlane( p2 );
if( p1 != p2 )
{
2012-11-26 18:58:24 +00:00
break;
}
bool found = false;
for( int w1i = 0; w1i < w.GetNumPoints(); w1i++ )
{
for( int w2i = 0; w2i < w2.GetNumPoints(); w2i++ )
{
if( CompareVec5( w[w1i], w2[w2i] ) && CompareVec5( w[( w1i + 1 ) % w.GetNumPoints()], w2[( w2i + 2 ) % w2.GetNumPoints()] ) )
{
w.InsertPoint( w2[( w2i + 1 ) % w2.GetNumPoints()], ( w1i + 1 ) % w.GetNumPoints() );
2012-11-26 18:58:24 +00:00
j = k;
found = true;
break;
}
}
if( found )
{
2012-11-26 18:58:24 +00:00
break;
}
}
if( !found )
{
2012-11-26 18:58:24 +00:00
break;
}
}
2012-11-26 18:58:24 +00:00
idRandom2 random( entityNumber );
Fracture_r( w, random );
}
}
2012-11-26 18:58:24 +00:00
physicsObj.SetContents( material->GetContentFlags() );
SetPhysics( &physicsObj );
}
/*
================
idBrittleFracture::FindNeighbours
================
*/
void idBrittleFracture::FindNeighbours()
{
2012-11-26 18:58:24 +00:00
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++ )
{
2012-11-26 18:58:24 +00:00
p1 = origin1 + w1[k].ToVec3() * axis1;
p2 = origin1 + w1[( k + 1 ) % w1.GetNumPoints()].ToVec3() * axis1;
2012-11-26 18:58:24 +00:00
dir = p2 - p1;
dir.Normalize();
axis = dir.ToMat3();
2012-11-26 18:58:24 +00:00
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 )
{
2012-11-26 18:58:24 +00:00
continue;
}
shard_t* shard2 = shards[j];
for( l = 0; l < shard1->neighbours.Num(); l++ )
{
if( shard1->neighbours[l] == shard2 )
{
2012-11-26 18:58:24 +00:00
break;
}
}
if( l < shard1->neighbours.Num() )
{
2012-11-26 18:58:24 +00:00
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-- )
{
2012-11-26 18:58:24 +00:00
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 )
{
2012-11-26 18:58:24 +00:00
shard1->neighbours.Append( shard2 );
shard1->edgeHasNeighbour[k] = true;
shard2->neighbours.Append( shard1 );
shard2->edgeHasNeighbour[( l - 1 + w2.GetNumPoints() ) % w2.GetNumPoints()] = true;
2012-11-26 18:58:24 +00:00
break;
}
}
}
}
}
}
for( k = 0; k < w1.GetNumPoints(); k++ )
{
if( !shard1->edgeHasNeighbour[k] )
{
2012-11-26 18:58:24 +00:00
break;
}
}
if( k < w1.GetNumPoints() )
{
2012-11-26 18:58:24 +00:00
shard1->atEdge = true;
}
else
{
2012-11-26 18:58:24 +00:00
shard1->atEdge = false;
}
}
}
/*
================
idBrittleFracture::Event_Activate
================
*/
void idBrittleFracture::Event_Activate( idEntity* activator )
{
2012-11-26 18:58:24 +00:00
disableFracture = false;
if( health <= 0 )
{
2012-11-26 18:58:24 +00:00
Break();
}
}
/*
================
idBrittleFracture::Event_Touch
================
*/
void idBrittleFracture::Event_Touch( idEntity* other, trace_t* trace )
{
2012-11-26 18:58:24 +00:00
idVec3 point, impulse;
2012-11-26 18:58:24 +00:00
// Let the server handle this, clients dont' predict it
if( common->IsClient() )
{
2012-11-26 18:58:24 +00:00
return;
}
if( !IsBroken() )
{
2012-11-26 18:58:24 +00:00
return;
}
if( trace->c.id < 0 || trace->c.id >= shards.Num() )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
point = shards[trace->c.id]->clipModel->GetOrigin();
impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass();
2012-11-26 18:58:24 +00:00
Shatter( point, impulse, gameLocal.time );
}
/*
================
idBrittleFracture::ClientThink
================
*/
void idBrittleFracture::ClientThink( const int curTime, const float fraction, const bool predict )
{
2012-11-26 18:58:24 +00:00
// only think forward because the state is not synced through snapshots
if( !gameLocal.isNewFrame )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
Think();
}
/*
================
idBrittleFracture::ClientPredictionThink
================
*/
void idBrittleFracture::ClientPredictionThink()
{
2012-11-26 18:58:24 +00:00
// only think forward because the state is not synced through snapshots
if( !gameLocal.isNewFrame )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
Think();
}
/*
================
idBrittleFracture::ClientReceiveEvent
================
*/
bool idBrittleFracture::ClientReceiveEvent( int event, int time, const idBitMsg& msg )
{
2012-11-26 18:58:24 +00:00
idVec3 point, dir;
switch( event )
{
case EVENT_PROJECT_DECAL:
{
2012-11-26 18:58:24 +00:00
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:
{
2012-11-26 18:58:24 +00:00
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:
{
2012-11-26 18:58:24 +00:00
return idEntity::ClientReceiveEvent( event, time, msg );
}
}
}