1119 lines
34 KiB
C++
1119 lines
34 KiB
C++
//===========================================================================
|
|
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
|
|
//
|
|
// Component: StatePropDSG
|
|
//
|
|
// Description: DSG object that contains a state prop
|
|
//
|
|
// Authors: Michael Riegger
|
|
//
|
|
//===========================================================================
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Includes
|
|
//===========================================================================
|
|
#include <memory/classsizetracker.h>
|
|
#include <radtime.hpp>
|
|
#include <render/DSG/StatePropDSG.h>
|
|
#include <console/console.h>
|
|
#include <stateprop/statepropdata.hpp>
|
|
#include <render/RenderManager/RenderManager.h>
|
|
#include <render/RenderManager/WorldRenderLayer.h>
|
|
#include <poser/pose.hpp>
|
|
#include <simcommon/simstatearticulated.hpp>
|
|
#include <simcollision/collisionobject.hpp>
|
|
#include <simphysics/articulatedphysicsobject.hpp>
|
|
#include <simphysics/physicsobject.hpp>
|
|
#include <worldsim/character/character.h>
|
|
#include <render/breakables/breakablesmanager.h>
|
|
#include <render/animentitydsgmanager/animentitydsgmanager.h>
|
|
#include <worldsim/coins/coinmanager.h>
|
|
#include <worldsim/worldphysicsmanager.h>
|
|
#include <ai/actor/actormanager.h>
|
|
#include <ai/actor/actoranimation.h>
|
|
#include <events/eventenum.h>
|
|
#include <data/persistentworldmanager.h>
|
|
#include <constants/StatePropEnum.h>
|
|
#include <worldsim/hitnrunmanager.h>
|
|
#include <sound/soundcollisiondata.h>
|
|
#include <worldsim/avatarmanager.h>
|
|
#include <events/eventdata.h>
|
|
#include <worldsim/avatar.h>
|
|
#include <mission/gameplaymanager.h>
|
|
#include <worldsim/redbrick/rootmatrixdriver.h>
|
|
|
|
|
|
//===========================================================================
|
|
// Local Constants, Typedefs, and Macros
|
|
//===========================================================================
|
|
|
|
// Amount to move the shadow/light pools off the ground
|
|
const float STATEPROP_SHADOW_Z_OFFSET = 1.0f;
|
|
|
|
//===========================================================================
|
|
// Global Data, Local Data, Local Classes
|
|
//===========================================================================
|
|
|
|
StatePropDSG::PoseMap* StatePropDSG::sp_SharedtPoses;
|
|
|
|
// How large the pool of tPoses is
|
|
const int MAX_NUM_SHARED_TPOSES = 20;
|
|
|
|
// Determine if a given physics object is a heavy duty ArticulatedPhysicsObject
|
|
// or not.
|
|
static bool IsFullArticulatedPhysicsObject( sim::PhysicsObject* object )
|
|
{
|
|
bool isArtPhys;
|
|
sim::ArticulatedPhysicsObject* artPhys = dynamic_cast< sim::ArticulatedPhysicsObject* >( object );
|
|
if ( artPhys != NULL )
|
|
{
|
|
isArtPhys = true;
|
|
}
|
|
else
|
|
{
|
|
isArtPhys = false;
|
|
}
|
|
return isArtPhys;
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
// Member Functions
|
|
//===========================================================================
|
|
|
|
StatePropDSG::StatePropDSG() :
|
|
mpStateProp( NULL ),
|
|
mpPhysObj( NULL ),
|
|
mpProcAnimator( 0 ),
|
|
m_ShadowElement( -1 ),
|
|
m_SimAnimJoint( -1 )
|
|
{
|
|
CLASSTRACKER_CREATE( StatePropDSG );
|
|
mTransform.Identity();
|
|
mPosn = mTransform.Row(3);
|
|
// Allocate some space for our array of shared tPoses
|
|
if ( sp_SharedtPoses == NULL )
|
|
{
|
|
sp_SharedtPoses = new PoseMap;
|
|
sp_SharedtPoses->reserve( MAX_NUM_SHARED_TPOSES );
|
|
}
|
|
}
|
|
|
|
StatePropDSG::~StatePropDSG()
|
|
{
|
|
CLASSTRACKER_DESTROY( StatePropDSG );
|
|
GetAnimEntityDSGManager()->Remove( this );
|
|
delete mpProcAnimator;
|
|
mpProcAnimator = 0;
|
|
|
|
// Ok, we are destroying a stateprop
|
|
|
|
|
|
if ( mpStateProp != NULL )
|
|
{
|
|
mpStateProp->RemoveStatePropListener( this );
|
|
mpStateProp->Release();
|
|
mpStateProp = NULL;
|
|
}
|
|
|
|
|
|
if ( mpPhysObj != NULL )
|
|
{
|
|
mpPhysObj->Release();
|
|
mpPhysObj = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StatePropDSG::LoadSetup( CStatePropData* statePropData,
|
|
int startState,
|
|
const rmt::Matrix& transform,
|
|
CollisionAttributes* ipCollAttr,
|
|
bool isDynaLoaded,
|
|
tEntityStore* ipSearchStore,
|
|
bool useSharedtPose,
|
|
sim::CollisionObject* collisionObject,
|
|
sim::PhysicsObject* physicsObject )
|
|
|
|
{
|
|
int type = ipCollAttr->GetClasstypeid();
|
|
mTransform = transform;
|
|
mPosn = transform.Row(3);
|
|
// Very likely that mTranslucent will always be true considering all the particles
|
|
// animations, fades, etc that are possible.
|
|
mTranslucent = true;
|
|
m_IsDynaLoaded = isDynaLoaded;
|
|
rAssert( mpStateProp == NULL );
|
|
// Memory optimization. If we are told to use a shared tPose, then
|
|
// try and grab an existing one (for this type) off out map
|
|
bool newSharedtPoseCreated;
|
|
tPose* tpose;
|
|
if ( useSharedtPose )
|
|
{
|
|
tpose = GetSharedtPose( statePropData->GetUID() );
|
|
if ( tpose == NULL )
|
|
// we want to share the pose, but not premade pose exists, so
|
|
// grab the pose that will be created in CreateStateProp
|
|
newSharedtPoseCreated = true;
|
|
else
|
|
newSharedtPoseCreated = false;
|
|
}
|
|
else
|
|
{
|
|
newSharedtPoseCreated = false;
|
|
tpose = NULL;
|
|
}
|
|
mpStateProp = CStateProp::CreateStateProp( statePropData, startState, tpose );
|
|
mpStateProp->SetUID( statePropData->GetUID() );
|
|
mpStateProp->AddStatePropListener( this );
|
|
|
|
// A new shared tPose was created, grab it off the composite drawable and insert it
|
|
// into our list
|
|
if ( newSharedtPoseCreated )
|
|
{
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable());
|
|
AddNewSharedtPose( statePropData->GetUID(), compDraw->GetPose() );
|
|
}
|
|
|
|
|
|
|
|
// Give the DSG object name the same as the stateprop object by default
|
|
SetName( mpStateProp->GetName() );
|
|
|
|
// Setup the physics, retrieve the pose
|
|
rAssert( dynamic_cast< tCompositeDrawable* > ( mpStateProp->GetDrawable()) );
|
|
tCompositeDrawable* pCompDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
tPose* p3dPose = pCompDraw->GetPose();
|
|
p3dPose->Evaluate();
|
|
|
|
|
|
// The sim library does in fact add something to the store, the above comment notwithstanding.
|
|
// So we wrap this call in a PushSection/PopSection.
|
|
//
|
|
if ( physicsObject == NULL )
|
|
physicsObject = p3d::find< sim::PhysicsObject >( statePropData->GetUID() );
|
|
|
|
if ( collisionObject == NULL )
|
|
collisionObject = p3d::find< sim::CollisionObject >( statePropData->GetUID() );
|
|
|
|
// Abort if there are no physics or collision objects found
|
|
if ( collisionObject == NULL )
|
|
return;
|
|
|
|
p3d::inventory->PushSection( );
|
|
p3d::inventory->SelectSection( "Default" );
|
|
|
|
sim::SimState* pSimState = NULL;
|
|
if ( type == PROP_MOVEABLE || type == PROP_ONETIME_MOVEABLE || type == PROP_BREAKABLE )
|
|
{
|
|
|
|
if ( IsFullArticulatedPhysicsObject( physicsObject ) )
|
|
{
|
|
poser::PoseEngine* poseEngine = new poser::PoseEngine(p3dPose , 1, p3dPose->GetNumJoint() );
|
|
poseEngine->AddRef();
|
|
RootMatrixDriver* rmd = new RootMatrixDriver( &mTransform );
|
|
rmd->AddRef();
|
|
poseEngine->AddPoseDriver( 0, rmd );
|
|
poseEngine->Begin();
|
|
|
|
poser::Pose* poserPose = poseEngine->GetPose();
|
|
|
|
// This is they expensive route, heavy duty articulated physics.
|
|
sim::SimStateArticulated* pSimStateArt = sim::SimStateArticulated::CreateSimStateArticulated( statePropData->GetUID(), poserPose, sim::SimStateAttribute_Default , ipSearchStore );
|
|
if ( pSimStateArt != NULL )
|
|
{
|
|
// Simplify calculations, we don't need or want expensive articulated computations
|
|
pSimStateArt->ConvertToRigidBody();
|
|
// Kill the poser::Pose object. Its *TONS* of useless matrices and garbage
|
|
pSimStateArt->ReleasePose();
|
|
pSimState = pSimStateArt;
|
|
}
|
|
else
|
|
{
|
|
// Fallback path
|
|
pSimState = sim::SimState::CreateSimState( statePropData->GetUID(), sim::SimStateAttribute_Default , ipSearchStore );
|
|
}
|
|
|
|
// Cleanup unused Articulated poseengine stuff
|
|
poseEngine->End();
|
|
// With no pose, the poseengine should be killed
|
|
poseEngine->ReleaseVerified();
|
|
poseEngine = NULL;
|
|
// No use for the root matrix driver either
|
|
rmd->ReleaseVerified();
|
|
rmd = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
rAssert( physicsObject != NULL );
|
|
// Try creating a simplified sim state (Ideal by far!)
|
|
pSimState = sim::SimState::CreateSimState( statePropData->GetUID(), sim::SimStateAttribute_Default , ipSearchStore );
|
|
}
|
|
|
|
// Place the collision object in the correct place
|
|
pSimState->SetTransform( transform );
|
|
}
|
|
else
|
|
{
|
|
pSimState = sim::SimStateArticulated::CreateStaticSimState( collisionObject );
|
|
// Tell the simstate where to place it in the world
|
|
pSimState->GetCollisionObject()->SetIsStatic(false);
|
|
pSimState->GetCollisionObject()->SetManualUpdate(false);
|
|
pSimState->SetTransform( transform );
|
|
pSimState->GetCollisionObject()->Update();
|
|
pSimState->GetCollisionObject()->GetCollisionVolume()->UpdateAll();
|
|
pSimState->GetCollisionObject()->SetIsStatic(true);
|
|
pSimState->GetCollisionObject()->SetManualUpdate(true);
|
|
}
|
|
rAssert( pSimState != NULL );
|
|
pSimState->SetControl( sim::simAICtrl );
|
|
|
|
// Lets hold onto the physics object, we'll need it for physics updates
|
|
// when in sim mode
|
|
mpPhysObj = static_cast< sim::PhysicsObject* >( pSimState->GetSimulatedObject() );
|
|
if ( mpPhysObj )
|
|
mpPhysObj->AddRef();
|
|
|
|
p3d::inventory->PopSection();
|
|
|
|
// Needed for SetSimState to work because of tRefCounted::Assign
|
|
pSimState->AddRef();
|
|
// The simstate holds the ai ref pointer of its parent object
|
|
// tell it what type of object this is
|
|
pSimState->mAIRefIndex = GetAIRef();
|
|
SetCollisionAttributes(ipCollAttr);
|
|
pSimState->mAIRefPointer = this;
|
|
// This sets the mpSimStateObj pointer and calls OnSetsimstate
|
|
SetSimState( pSimState );
|
|
pCompDraw->ProcessShaders(*this);
|
|
pSimState->Release();
|
|
|
|
// Add this object to the animation update list that gets called every frame
|
|
GetAnimEntityDSGManager()->Add( this );
|
|
|
|
}
|
|
|
|
StatePropDSG*
|
|
StatePropDSG::Clone(const char* Name, const rmt::Matrix& iMatrix) const
|
|
{
|
|
StatePropDSG* pClone = new StatePropDSG();
|
|
|
|
CStatePropData* spData = mpStateProp->GetStatePropData();
|
|
const int START_STATE = 0;
|
|
CollisionAttributes* pCollAttr = GetCollisionAttributes();
|
|
bool usingSharedPose;
|
|
|
|
// Should we share the pose?
|
|
// That depends on whether the original is sharing a pose
|
|
if ( GetSharedtPose( spData->GetUID() ) != NULL )
|
|
usingSharedPose = true;
|
|
else
|
|
usingSharedPose = false;
|
|
|
|
pClone->LoadSetup( spData, START_STATE, iMatrix, pCollAttr, true, NULL, usingSharedPose, NULL, NULL );
|
|
pClone->SetName( Name );
|
|
|
|
return pClone;
|
|
}
|
|
|
|
void StatePropDSG::OnSetSimState( sim::SimState* ipSimState )
|
|
{
|
|
tRefCounted::Assign( mpSimStateObj, ipSimState );
|
|
mpSimStateObj->mAIRefIndex = this->GetAIRef();
|
|
mpSimStateObj->ResetVelocities();
|
|
|
|
// set up box and sphere for DSG
|
|
//
|
|
// note, unlike the StaticPhysDSG, this box and sphere are in object space
|
|
rmt::Vector center;
|
|
if ( mpPhysObj != NULL )
|
|
{
|
|
center = mpPhysObj->GetExternalCMOffset();
|
|
}
|
|
else
|
|
{
|
|
center = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mPosition;
|
|
center -= mPosn;
|
|
}
|
|
float radius = mpSimStateObj->GetSphereRadius();
|
|
|
|
mBBox.low = center;
|
|
mBBox.high = center;
|
|
mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
|
|
mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
|
|
|
|
mSphere.centre = center;
|
|
mSphere.radius = radius;
|
|
|
|
rAssert(mpCollisionAttributes);
|
|
mpSimStateObj->SetPhysicsProperties(mpCollisionAttributes->GetPhysicsProperties());
|
|
}
|
|
|
|
void
|
|
StatePropDSG::Display()
|
|
{
|
|
|
|
#ifdef PROFILER_ENABLED
|
|
char profileName[] = " StatePropDSG Display";
|
|
#endif
|
|
if(IS_DRAW_LONG) return;
|
|
DSG_BEGIN_PROFILE(profileName)
|
|
if(CastsShadow())
|
|
{
|
|
BillboardQuadManager::Enable();
|
|
}
|
|
p3d::stack->PushMultiply( mTransform );
|
|
mpStateProp->Display( mpProcAnimator );
|
|
p3d::stack->Pop();
|
|
if(CastsShadow())
|
|
{
|
|
BillboardQuadManager::Disable();
|
|
DisplaySimpleShadow();
|
|
}
|
|
DSG_END_PROFILE(profileName)
|
|
}
|
|
|
|
void
|
|
StatePropDSG::AdvanceAnimation( float timeInMS )
|
|
{
|
|
// Simple animation frame controller advancement
|
|
mpStateProp->Update( timeInMS );
|
|
if( mpProcAnimator )
|
|
{
|
|
mpProcAnimator->Advance( timeInMS );
|
|
}
|
|
if ( m_SimAnimJoint != -1 )
|
|
{
|
|
AnimateCollisionVolume();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StatePropDSG::Update( float timeInSec )
|
|
{
|
|
// Update physics and collision, if necessary
|
|
if ( mpPhysObj != NULL )
|
|
{
|
|
mpPhysObj->Update( timeInSec );
|
|
}
|
|
|
|
if ( mpSimStateObj )
|
|
{
|
|
sim::CollisionObject* collObj = mpSimStateObj->GetCollisionObject();
|
|
if ( collObj->HasMoved( ) || collObj->HasRelocated( ) )
|
|
{
|
|
collObj->Update();
|
|
|
|
// do this _before_ updating matrix!
|
|
rmt::Box3D oldBox;
|
|
|
|
GetBoundingBox( &oldBox );
|
|
|
|
mTransform = mpSimStateObj->GetTransform();
|
|
mPosn = mpSimStateObj->GetPosition();
|
|
|
|
WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
|
|
// Sanity check
|
|
rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
|
|
pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
|
|
}
|
|
|
|
}
|
|
|
|
if(mIsHit && (GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE))
|
|
{
|
|
if(!GetGameplayManager()->TestPosInFrustrumOfPlayer( mPosn, 0, mSphere.radius))
|
|
{
|
|
this->AddRef();
|
|
((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->RemoveGuts(this);
|
|
GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
|
|
GetRenderManager()->mEntityDeletionList.Add(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
StatePropDSG::GetBoundingBox( rmt::Box3D* box )
|
|
{
|
|
|
|
(box->low).Add(mBBox.low, mPosn);
|
|
(box->high).Add(mBBox.high, mPosn);
|
|
}
|
|
void
|
|
StatePropDSG::GetBoundingSphere( rmt::Sphere* out_sphere )
|
|
{
|
|
(out_sphere->centre).Add(mSphere.centre, mPosn);
|
|
|
|
out_sphere->radius = mSphere.radius;
|
|
}
|
|
|
|
rmt::Vector*
|
|
StatePropDSG::pPosition()
|
|
{
|
|
rAssert( false );
|
|
return &mTransform.Row(3);
|
|
}
|
|
|
|
|
|
const rmt::Vector&
|
|
StatePropDSG::rPosition()
|
|
{
|
|
return mTransform.Row(3);
|
|
}
|
|
|
|
void
|
|
StatePropDSG::GetPosition( rmt::Vector* ipPosn )
|
|
{
|
|
rAssert( ipPosn != NULL );
|
|
*ipPosn = mTransform.Row(3);
|
|
}
|
|
|
|
void
|
|
StatePropDSG::SetPosition( const rmt::Vector& position )
|
|
{
|
|
rmt::Matrix newTransform = mTransform;
|
|
newTransform.FillTranslate( position );
|
|
SetTransform( newTransform );
|
|
}
|
|
|
|
void
|
|
StatePropDSG::GetTransform( rmt::Matrix* pTransform )
|
|
{
|
|
rAssert( pTransform != NULL );
|
|
*pTransform = mTransform;
|
|
}
|
|
|
|
void
|
|
StatePropDSG::SetTransform( const rmt::Matrix& transform )
|
|
{
|
|
|
|
|
|
rmt::Box3D oldBox;
|
|
GetBoundingBox( &oldBox );
|
|
|
|
// Remember old manual update settings
|
|
bool manualUpdate = mpSimStateObj->GetCollisionObject()->IsManualUpdate();
|
|
mpSimStateObj->GetCollisionObject()->SetManualUpdate( false );
|
|
|
|
if ( mpSimStateObj->GetControl() == sim::simAICtrl )
|
|
{
|
|
mpSimStateObj->SetTransform( transform );
|
|
}
|
|
else
|
|
{
|
|
// Can't just tell an object to move to a new position
|
|
// if we are in sim control
|
|
// force it to ai, set transform, then set it back
|
|
mpSimStateObj->SetControl( sim::simAICtrl );
|
|
mpSimStateObj->SetTransform( transform );
|
|
mpSimStateObj->SetControl( sim::simSimulationCtrl );
|
|
}
|
|
mpSimStateObj->ResetVelocities();
|
|
|
|
// Matrix is reset, recompute the bounding box and move it
|
|
// in the DSG( Warning, this assumes that the user actually has it IN
|
|
// the DSG. This was always the case for DSG objects so lets not break
|
|
// tradition
|
|
// Bounding box relies on mPosn, lets update it
|
|
|
|
mTransform = mpSimStateObj->GetTransform(-1);
|
|
mPosn = mTransform.Row( 3 );
|
|
|
|
|
|
WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
|
|
// Sanity check
|
|
|
|
rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
|
|
pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
|
|
|
|
// Restore manual update settings
|
|
mpSimStateObj->GetCollisionObject()->SetManualUpdate( manualUpdate );
|
|
}
|
|
|
|
|
|
sim::Solving_Answer
|
|
StatePropDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
|
|
{
|
|
// Make sure the object doesn't collide with other objects that it may be touching that are
|
|
// part of the static world
|
|
if(this->GetSimState()->GetControl() == sim::simAICtrl && pCollidedObj->GetCollisionObject()->IsStatic() == true)
|
|
{
|
|
return sim::Solving_Aborted;
|
|
}
|
|
|
|
switch ( pCollidedObj->mAIRefIndex )
|
|
{
|
|
case PhysicsAIRef::PlayerCharacter:
|
|
case PhysicsAIRef::NPCharacter:
|
|
{
|
|
rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer) ) );
|
|
Character* pCharacter = static_cast< Character* >( pCollidedObj->mAIRefPointer );
|
|
return sim::Solving_Aborted;
|
|
}
|
|
case PhysicsAIRef::redBrickVehicle:
|
|
{
|
|
// Collision with a vehicle
|
|
// is this prop a breakable?
|
|
if ( GetType() == PROP_BREAKABLE )
|
|
{
|
|
Vehicle* vehicle = static_cast< Vehicle* >( pCollidedObj->mAIRefPointer );
|
|
|
|
if ( HandleEvent( VEHICLE_HIT ) == false )
|
|
{
|
|
// goto the destroyed state
|
|
HandleEvent( DESTROYED );
|
|
HandleEvent( STOMP );
|
|
HandleEvent( HIT );
|
|
}
|
|
|
|
//
|
|
// Cue the breaking sounds
|
|
//
|
|
SoundCollisionData soundData( 1.0f, vehicle, this );
|
|
|
|
GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
|
|
|
|
// return solving aborted, the breakable isn't stopping this vehicle
|
|
return sim::Solving_Aborted;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
HandleEvent( StatePropDSG::FEATHER_TOUCH );
|
|
|
|
// need this here?
|
|
this->mIsHit = true;
|
|
|
|
// Breakables always stay put and animate in place
|
|
if ( GetType() != PROP_BREAKABLE )
|
|
AddToSimulation( );
|
|
return sim::Solving_Continue;
|
|
}
|
|
|
|
|
|
sim::Solving_Answer
|
|
StatePropDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
|
|
{
|
|
// Calculate impulse
|
|
float impulseMagSqr = impulse.MagnitudeSqr();
|
|
|
|
if ( impulseMagSqr > 10000 )
|
|
{
|
|
sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
|
|
sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
|
|
|
|
sim::SimState* simStateA = collObjA->GetSimState();
|
|
sim::SimState* simStateB = collObjB->GetSimState();
|
|
|
|
// Has the stateprop been hit by a vehicle?
|
|
// If so, notify the stateprop
|
|
if ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle ||
|
|
simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
|
|
{
|
|
bool msgHandled = HandleEvent( VEHICLE_HIT );
|
|
if ( msgHandled == false )
|
|
HandleEvent( HIT );
|
|
}
|
|
else
|
|
{
|
|
// Tell the stateprop that a normal event was triggered
|
|
HandleEvent( HIT );
|
|
}
|
|
}
|
|
|
|
|
|
return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
|
|
}
|
|
|
|
void
|
|
StatePropDSG::GenerateCoins( int numCoins )
|
|
{
|
|
HitnRunManager* hrm = GetHitnRunManager();
|
|
|
|
// This is just so bad.
|
|
//Since an object has no idea what it really is, we can't tell if the
|
|
//breakable object is a crate, tree, or krusty glass, etc. So to try
|
|
//and pick out the crates we'll look at the sound file played when the
|
|
//object breaks and if it's car_hit_crate, we'll ASSUME that it's a
|
|
//crate. *sigh*
|
|
bool isCrate = false;
|
|
bool coinsDisabled = hrm->IsHitnRunDisabled();
|
|
CollisionAttributes* collAttribs = this->GetCollisionAttributes();
|
|
if(collAttribs)
|
|
{
|
|
if((strcmp(collAttribs->GetSound(), "car_hit_crate")) == 0 && (mPersistentObjectID >= 0))
|
|
{
|
|
isCrate = true;
|
|
}
|
|
}
|
|
|
|
if(!coinsDisabled || isCrate)
|
|
{
|
|
GetCoinManager()->SpawnCoins( numCoins + (coinsDisabled && isCrate ? 1 : 0), rPosition() );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
StatePropDSG::RecieveEvent( int callback , CStateProp* stateProp )
|
|
{
|
|
// The artists are trying to tell us something!
|
|
// These are callbacks set from the stateprop builder tool
|
|
// switch on the callback type and perform the appropriate action
|
|
|
|
switch ( callback )
|
|
{
|
|
case StatePropEnum::eStateChange:
|
|
break;
|
|
|
|
case StatePropEnum::eRemoveFromWorld:
|
|
GetBreakablesManager()->RemoveBrokenObjectFromWorld( this, RenderEnums::LevelSlot, m_IsDynaLoaded );
|
|
GetPersistentWorldManager()->OnObjectBreak( mPersistentObjectID );
|
|
break;
|
|
case StatePropEnum::eSpawn1Coin:
|
|
GenerateCoins( 1 );
|
|
break;
|
|
case StatePropEnum::eSpawn5Coins:
|
|
GenerateCoins( 5 );
|
|
break;
|
|
case StatePropEnum::eSpawn10Coins:
|
|
GenerateCoins( 10 );
|
|
break;
|
|
case StatePropEnum::eSpawn15Coins:
|
|
GenerateCoins( 15 );
|
|
break;
|
|
case StatePropEnum::eSpawn20Coins:
|
|
GenerateCoins( 20 );
|
|
break;
|
|
case StatePropEnum::eRemoveCollisionVolume:
|
|
{
|
|
EnableCollisionVolume( false );
|
|
GetPersistentWorldManager()->OnObjectBreak( mPersistentObjectID );
|
|
}
|
|
break;
|
|
case StatePropEnum::eRemoveFirstCollisionVolume:
|
|
{
|
|
RemoveSubCollisionVolume( 0 );
|
|
}
|
|
break;
|
|
case StatePropEnum::eRemoveSecondCollisionVolume:
|
|
{
|
|
RemoveSubCollisionVolume( 1 );
|
|
}
|
|
break;
|
|
case StatePropEnum::eRemoveThirdCollisionVolume:
|
|
{
|
|
RemoveSubCollisionVolume( 2 );
|
|
}
|
|
break;
|
|
case StatePropEnum::eKillSpeed:
|
|
if ( mpSimStateObj != NULL )
|
|
{
|
|
mpSimStateObj->ResetVelocities();
|
|
mpSimStateObj->SetControl( sim::simAICtrl );
|
|
}
|
|
break;
|
|
case StatePropEnum::eRadiateForce:
|
|
{
|
|
const float FORCE_RADIUS = 1.5f;
|
|
ReserveArray< DynaPhysDSG* > dynamicsList( 200 );
|
|
// Alpha hack to make the head (jeb's statute) go into SIM but nothing else.
|
|
// Benches have bad volumes so they look stupid when they go into sim control
|
|
rmt::Vector forcePosition( 135.16f, 13.1979f, 37.911f );
|
|
GetIntersectManager()->FindDynaPhysElems( forcePosition, FORCE_RADIUS, dynamicsList );
|
|
for ( int i = 0 ; i < dynamicsList.mUseSize ; i++ )
|
|
{
|
|
if ( dynamicsList[i]->GetAIRef() != PhysicsAIRef::PlayerCharacter )
|
|
{
|
|
dynamicsList[i]->ApplyForce( rmt::Vector(0,1,0), 200.0f );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case StatePropEnum::eEmitLeaves:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case StatePropEnum::eSpawn5CoinsZ:
|
|
{
|
|
int numCoins = 5;
|
|
rmt::Vector coinDirection = -mTransform.Row(2);
|
|
GetCoinManager()->SpawnCoins( numCoins, rPosition(), rPosition().y, &coinDirection );
|
|
}
|
|
break;
|
|
case StatePropEnum::eColaDestroyed:
|
|
// A cola object (crate/vending machine) was destroyed. For some reason, people actually
|
|
// care about this. (Hit and Run meter I think) fire off an event
|
|
// signalling this
|
|
GetEventManager()->TriggerEvent( EVENT_COLAPROP_DESTROYED, this );
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
void
|
|
StatePropDSG::AddToSimulation()
|
|
{
|
|
// Only valid if this object is flagged as moveable
|
|
if ( GetType() == PROP_MOVEABLE || GetType() == PROP_ONETIME_MOVEABLE)
|
|
{
|
|
if ( mpSimStateObj->GetControl() == sim::simAICtrl )
|
|
{
|
|
mpSimStateObj->SetControl(sim::simSimulationCtrl);
|
|
if ( mGroundPlaneIndex != -1 )
|
|
{
|
|
//mGroundPlaneIndex = FetchGroundPlane();
|
|
GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
|
|
}
|
|
//GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
|
|
GetEventManager()->TriggerEvent( EVENT_STATEPROP_ADDED_TO_SIM, this );
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
StatePropDSG::ApplyForce( const rmt::Vector& direction, float force )
|
|
{
|
|
if ( force >= 800.0f )
|
|
{
|
|
HandleEvent( StatePropDSG::STOMP );
|
|
}
|
|
|
|
if ( force > 400.0f )
|
|
{
|
|
HandleEvent( StatePropDSG::DESTROYED );
|
|
HandleEvent( StatePropDSG::HIT );
|
|
}
|
|
else
|
|
{
|
|
HandleEvent( StatePropDSG::HIT );
|
|
}
|
|
if ( GetType() != PROP_BREAKABLE )
|
|
{
|
|
DynaPhysDSG::ApplyForce( direction, force );
|
|
}
|
|
}
|
|
|
|
int
|
|
StatePropDSG::CastsShadow()
|
|
{
|
|
// StatepropDSGs can use a joint as a shadow
|
|
// if the m_ShadowElement variable is not -1, then
|
|
// we want to use the joint as a shadow
|
|
int retVal;
|
|
if ( m_ShadowElement == -1 )
|
|
retVal = 0;
|
|
else
|
|
retVal = 1;
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void
|
|
StatePropDSG::DisplaySimpleShadow()
|
|
{
|
|
BEGIN_PROFILE("DisplaySimpleShadow")
|
|
p3d::pddi->SetZWrite(false);
|
|
if ( mpShadowMatrix != NULL )
|
|
{
|
|
rAssert( m_ShadowElement != -1 );
|
|
// Activate the joint's visibility
|
|
// DONT TOUCH THE LOCK SETTINGS, the stateprop will lock visibility
|
|
// off. Don't override the artists
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
|
|
|
|
element->SetVisibility( true );
|
|
|
|
// the camera
|
|
rmt::Vector camPos;
|
|
p3d::context->GetView()->GetCamera()->GetWorldPosition( &camPos );
|
|
camPos.Sub( mpShadowMatrix->Row(3) );
|
|
camPos.Normalize();
|
|
rmt::Matrix toCamera;
|
|
toCamera.Identity();
|
|
toCamera.FillTranslate( camPos * STATEPROP_SHADOW_Z_OFFSET );
|
|
|
|
// Final shadow transform = position/orientation * tocamera translation
|
|
rmt::Matrix shadowTransform( *mpShadowMatrix );
|
|
shadowTransform.Mult( toCamera );
|
|
|
|
// Display
|
|
p3d::stack->PushMultiply( shadowTransform );
|
|
element->Display();
|
|
p3d::stack->Pop();
|
|
element->SetVisibility( false );
|
|
}
|
|
else
|
|
{
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
|
|
compDraw->GetPose()->SetPoseReady( false );
|
|
compDraw->GetPose()->Evaluate( &mTransform );
|
|
const rmt::Matrix* worldMatrix = element->GetWorldMatrix();
|
|
mpShadowMatrix = CreateShadowMatrix( worldMatrix->Row(3) );
|
|
compDraw->GetPose()->SetPoseReady( false );
|
|
if ( mpShadowMatrix == NULL )
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
p3d::pddi->SetZWrite(true);
|
|
END_PROFILE("DisplaySimpleShadow")
|
|
}
|
|
|
|
void
|
|
StatePropDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& irRefVect)
|
|
{
|
|
if ( CastsShadow() )
|
|
{
|
|
mRank = FLT_MAX;
|
|
}
|
|
else
|
|
{
|
|
IEntityDSG::SetRank( irRefPosn, irRefVect );
|
|
}
|
|
}
|
|
|
|
void
|
|
StatePropDSG::SetShadowElement( tUID elementName )
|
|
{
|
|
// Lets use the given joint as a shadow / light pool!
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
|
|
m_ShadowElement = compDraw->FindNodeIndex( elementName );
|
|
rAssert( m_ShadowElement != -1 );
|
|
tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
|
|
|
|
element->SetPose( compDraw->GetPose() );
|
|
compDraw->GetPose()->SetPoseReady( false );
|
|
compDraw->GetPose()->Evaluate( &mTransform );
|
|
// Each drawable elements pose is set upon call to tCompositeDrawable::
|
|
const rmt::Matrix* worldMatrix = element->GetWorldMatrix();
|
|
rAssert( mpShadowMatrix == NULL );
|
|
mpShadowMatrix = CreateShadowMatrix( worldMatrix->Row(3) );
|
|
|
|
// set visibility to false so it won't be drawn as part of the regular pass
|
|
// Get the drawable element
|
|
element->SetVisibility( false );
|
|
compDraw->GetPose()->SetPoseReady( false );
|
|
}
|
|
|
|
|
|
// Turn collisions on and off. Pretty useful if you want to
|
|
// attach stateprops to vehicles as collectibles
|
|
// or when they get destroyed
|
|
void
|
|
StatePropDSG::EnableCollisionVolume( bool enable )
|
|
{
|
|
sim::SimState* pSimState = GetSimState();
|
|
if ( pSimState == NULL )
|
|
return;
|
|
|
|
sim::CollisionObject* pCollisionObject = pSimState->GetCollisionObject();
|
|
if ( pCollisionObject == NULL )
|
|
return;
|
|
|
|
pCollisionObject->SetCollisionEnabled( enable );
|
|
}
|
|
|
|
void
|
|
StatePropDSG::RemoveSubCollisionVolume( int volumeIndex )
|
|
{
|
|
sim::SimState* pSimState = GetSimState();
|
|
rTuneAssert( pSimState != NULL );
|
|
|
|
sim::CollisionObject* pCollisionObject = pSimState->GetCollisionObject();
|
|
rTuneAssert( pCollisionObject != NULL );
|
|
|
|
// Find the collision volume, then grab its subcollisionvolume
|
|
sim::CollisionVolume* primaryVolume = pCollisionObject->GetCollisionVolume();
|
|
|
|
sim::TList<sim::CollisionVolume*>* pSubVolumeList = primaryVolume->SubVolumeList();
|
|
rTuneAssert( volumeIndex < pSubVolumeList->GetSize() && volumeIndex >= 0 );
|
|
if ( volumeIndex >= 0 && volumeIndex < pSubVolumeList->GetSize() )
|
|
{
|
|
sim::CollisionVolume* subVolume = pSubVolumeList->GetAt( volumeIndex );
|
|
rTuneAssert( subVolume != NULL );
|
|
if ( subVolume != NULL )
|
|
{
|
|
// Kill the subvolume
|
|
primaryVolume->RemoveSubVolume( subVolume );
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
StatePropDSG::GetAIRef()
|
|
{
|
|
return PhysicsAIRef::StateProp;
|
|
}
|
|
|
|
|
|
// Force an animation state change
|
|
void
|
|
StatePropDSG::SetState( int state )
|
|
{
|
|
mpStateProp->SetState( state );
|
|
}
|
|
|
|
// Lets send this thing an event and hope that the artists
|
|
// have everything set up to handle it
|
|
bool
|
|
StatePropDSG::HandleEvent( Event eventID )
|
|
{
|
|
return mpStateProp->OnEvent( eventID );
|
|
}
|
|
|
|
unsigned int
|
|
StatePropDSG::GetState()const
|
|
{
|
|
return mpStateProp->GetState();
|
|
}
|
|
|
|
// Used if other people are interested in the messages generated from the
|
|
// stateprop subsystem
|
|
void
|
|
StatePropDSG::AddStatePropListener( CStatePropListener* statePropListener )
|
|
{
|
|
if ( statePropListener != NULL && mpStateProp != NULL )
|
|
{
|
|
mpStateProp->AddStatePropListener( statePropListener );
|
|
}
|
|
}
|
|
|
|
void
|
|
StatePropDSG::RemoveStatePropListener( CStatePropListener* statePropListener )
|
|
{
|
|
if ( statePropListener != NULL && mpStateProp != NULL )
|
|
{
|
|
mpStateProp->RemoveStatePropListener( statePropListener );
|
|
}
|
|
}
|
|
|
|
// Sets which joint the sim state should animate upon
|
|
void
|
|
StatePropDSG::SetSimJoint( int jointId )
|
|
{
|
|
if ( GetType() != PROP_STATIC )
|
|
m_SimAnimJoint = jointId;
|
|
|
|
}
|
|
|
|
int
|
|
StatePropDSG::GetType()const
|
|
{
|
|
return GetCollisionAttributes()->GetClasstypeid();
|
|
}
|
|
|
|
// Turn on/off visibility
|
|
void
|
|
StatePropDSG::EnableVisibility( bool enable )
|
|
{
|
|
// Iterate through the composite drawable and set visiblity on each one to
|
|
// the input
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
rAssert( compDraw != NULL );
|
|
for ( int i = 0 ; i < compDraw->GetNumDrawableElement() ; i++ )
|
|
{
|
|
tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement(i);
|
|
if ( element )
|
|
element->SetVisibility( enable );
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StatePropDSG::RemoveAllSharedtPoses()
|
|
{
|
|
|
|
if ( sp_SharedtPoses )
|
|
{
|
|
PoseMapIt it;
|
|
// We addrefed each tPose when we put them onto the array, now release them
|
|
for ( it = sp_SharedtPoses->begin() ; it != sp_SharedtPoses->end() ; it++ )
|
|
{
|
|
it->second->Release();
|
|
}
|
|
delete sp_SharedtPoses;
|
|
sp_SharedtPoses = NULL;
|
|
}
|
|
}
|
|
|
|
// Uses m_SimJoint to animate the collision volume
|
|
void
|
|
StatePropDSG::AnimateCollisionVolume()
|
|
{
|
|
rAssert( m_SimAnimJoint != -1 );
|
|
// We want to call SimState::SetTransform with the world space transform
|
|
// of the evaluated joint
|
|
// So first evaluate the composite drawables pose
|
|
tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
|
|
rAssert( compDraw != NULL );
|
|
tPose* pose = compDraw->GetPose();
|
|
pose->Evaluate();
|
|
tPose::Joint* joint = pose->GetJoint( m_SimAnimJoint );
|
|
// Now grab the world matrix and apply it to the simstate
|
|
rmt::Matrix simTransform = mTransform;
|
|
simTransform.Row(3) += joint->worldMatrix.Row(3);
|
|
|
|
sim::SimState* simState = GetSimState();
|
|
|
|
// Before setting the transform, make sure to set manual updates to true
|
|
bool origManualUpdate = simState->GetCollisionObject()->IsManualUpdate();
|
|
simState->GetCollisionObject()->SetManualUpdate( false );
|
|
|
|
simState->SetTransform( simTransform );
|
|
simState->GetCollisionObject()->SetManualUpdate( origManualUpdate );
|
|
|
|
// Call reset velocities.
|
|
// Other the sim state is going to get a velocity based upon the distance I just moved it
|
|
simState->ResetVelocities();
|
|
}
|
|
|
|
|
|
tPose*
|
|
StatePropDSG::GetSharedtPose( tUID type )
|
|
{
|
|
tPose* pose;
|
|
PoseMapIt it = sp_SharedtPoses->find( type );
|
|
if ( it != sp_SharedtPoses->end() )
|
|
{
|
|
pose = it->second;
|
|
}
|
|
else
|
|
{
|
|
pose = NULL;
|
|
}
|
|
return pose;
|
|
}
|
|
|
|
// Add the given shared pose to the shared pose list
|
|
void
|
|
StatePropDSG::AddNewSharedtPose( tUID type, tPose* pose )
|
|
{
|
|
if(sp_SharedtPoses->find(type) == sp_SharedtPoses->end())
|
|
{
|
|
pose->AddRef();
|
|
sp_SharedtPoses->insert( type, pose );
|
|
}
|
|
else
|
|
{
|
|
rAssert(0);
|
|
}
|
|
}
|