thug/Code/Gel/Components/modelcomponent.cpp

1841 lines
63 KiB
C++
Raw Normal View History

2016-02-13 21:39:12 +00:00
//****************************************************************************
//* MODULE: Gel/Components
//* FILENAME: ModelComponent.cpp
//* OWNER: Gary Jesdanun
//* CREATION DATE: 10/17/2002
//****************************************************************************
#include <gel/components/modelcomponent.h>
#include <core/string/stringutils.h>
#include <gel/object/compositeobject.h>
#include <gel/object/compositeobjectmanager.h>
#include <gel/components/animationcomponent.h>
#include <gel/components/skeletoncomponent.h>
#include <gel/components/suspendcomponent.h>
#include <gel/net/server/netserv.h>
#include <gel/net/client/netclnt.h>
#include <gel/scripting/array.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/script.h>
#include <gel/scripting/struct.h>
#include <gel/scripting/utils.h>
#include <gfx/gfxutils.h>
#include <gfx/modelappearance.h>
#include <gfx/modelbuilder.h>
#include <gfx/nx.h>
#include <gfx/nxgeom.h>
#include <gfx/nxlight.h>
#include <gfx/nxmodel.h>
#include <gfx/skeleton.h>
#include <sk/gamenet/gamenet.h>
#include <sk/engine/feeler.h>
#include <sk/modules/skate/skate.h>
#include <sk/objects/moviecam.h>
namespace Obj
{
#define vMAX_PATH (512)
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::init_model_from_level_object( uint32 checksumName )
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
Dbg_MsgAssert( p_sector, ( "WARNING: sGetSector(0x%x) returned NULL (%s)\n", checksumName, Script::FindChecksumName(checksumName) ) );
if ( p_sector )
{
// need to clone the source, not the instance?
Nx::CGeom* pGeom = p_sector->GetGeom();
if( pGeom )
{
Nx::CGeom* pClonedGeom = pGeom->Clone( true );
pClonedGeom->SetActive(true);
mp_model->AddGeom( pClonedGeom, 0 );
m_isLevelObject = true;
}
// Also get the collision data pointer
// Dbg_Assert(p_sector->GetCollSector());
// Nx::CCollObjTriData* p_coll_tri_data = p_sector->GetCollSector()->GetGeometry();
// Dbg_Assert(p_coll_tri_data);
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// This static function is what is registered with the component factory
// object, (currently the CCompositeObjectManager)
CBaseComponent * CModelComponent::s_create()
{
return static_cast<CBaseComponent*>(new CModelComponent);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CModelComponent::CModelComponent() : CBaseComponent()
{
SetType(CRCD(0x286a8d26,"Model"));
mp_model = Nx::CEngine::sInitModel();
// since it's uninitialized
m_display_rotation_offset.Set( 0.0f, 0.0f, 0.0f );
mpDisplayRotationInfo[0].Clear();
mpDisplayRotationInfo[1].Clear();
mpDisplayRotationInfo[2].Clear();
m_numLODs = 0;
m_isLevelObject = false;
mDisplayOffset.Set();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CModelComponent::~CModelComponent()
{
Dbg_MsgAssert( mp_model, ( "No model" ) );
Nx::CEngine::sUninitModel( mp_model );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::InitFromStructure( Script::CStruct* pParams )
{
// needs to come before the geoms get added,
// because otherwise we've cause some fragmentaton
// when we switch models...
if ( pParams->ContainsFlag( CRCD(0x10079f2d,"UseModelLights") ) )
{
if ( !mp_model->GetModelLights() )
{
mp_model->CreateModelLights();
Nx::CModelLights *p_lights = mp_model->GetModelLights();
p_lights->SetPositionPointer(&GetObject()->m_pos);
}
}
else
{
if ( mp_model->GetModelLights() )
{
mp_model->DestroyModelLights();
}
}
// this function assumes that the skeletoncomponent
// will be correctly added BEFORE the modelcomponent
// not all models are initialized using InitFromStructure,
// so we should find someplace better to put the following
// call to SetSkeleton()
CSkeletonComponent *p_skeleton_component = GetSkeletonComponentFromObject(GetObject());
if (p_skeleton_component)
{
GetModel()->SetSkeleton( p_skeleton_component->GetSkeleton() );
}
int doShadowVolume = 0;
pParams->GetInteger( CRCD(0x2a1f92c0,"shadowVolume"), &doShadowVolume, Script::NO_ASSERT );
mp_model->EnableShadowVolume( doShadowVolume );
InitModel( pParams );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::InitModel( Script::CStruct* pParams )
{
// should destroy the existing model
mp_model->ClearGeoms();
// update its scale, if any
// (need to do this before init_model(),
// which can potentially override the scale)
Mth::Vector theScale(1.0f,1.0f,1.0f);
if ( Gfx::GetScaleFromParams( &theScale, pParams ) )
{
mp_model->SetScale( theScale );
}
const char* pModelName;
uint32 model_name_checksum=0;
uint32 assetName;
uint32 refObjectName;
uint32 cloneID;
// check first if it is a LevelObject
// in which case it will have a "name" which is the name of the sector
uint32 ClassChecksum = 0;
pParams->GetChecksum( CRCD(0x12b4e660,"Class"), &ClassChecksum );
if ( ClassChecksum == CRCD(0xb7b3bd86,"LevelObject") )
{
uint32 checksumName;
pParams->GetChecksum( CRCD(0xa1dc81f9,"Name"), &checksumName, Script::ASSERT );
init_model_from_level_object(checksumName);
}
else
{
// by default, use the asset manager
int useAssetManager = 1;
pParams->GetInteger( CRCD(0xc63c8d38,"use_asset_manager"), &useAssetManager, Script::NO_ASSERT );
int texDictOffset = 0;
pParams->GetInteger( CRCD(0xf891ac27,"texDictOffset"), &texDictOffset, Script::NO_ASSERT );
if ( pParams->GetChecksum( CRCD(0x153a84de,"refObjectName"), &refObjectName, Script::NO_ASSERT ) )
{
m_refObjectName = refObjectName;
m_hasRefObject = true;
mp_model->SetRenderMode( Nx::vNONE );
}
else if ( pParams->GetChecksum( CRCD(0x86bd5b8f,"assetName"), &assetName, Script::NO_ASSERT ) )
{
// this means that the asset name was faked somehow
// (for example, if we've loaded it up from a data stream
// instead of a filename)
Dbg_MsgAssert( mp_model, ( "No model" ) );
int supportMultipleMaterialColors = 0;
pParams->GetInteger( CRCD(0x92b43e79,"multicolor"), &supportMultipleMaterialColors, Script::NO_ASSERT );
// component name doesn't matter... give it a dummy
uint32 componentName = 0;
mp_model->AddGeom( assetName, componentName, supportMultipleMaterialColors );
}
else if ( pParams->GetChecksum( CRCD(0x7dd037b3,"cloneFrom"), &cloneID, Script::NO_ASSERT ) )
{
// Clone the geometry off of an existing model
CCompositeObject* pObject = static_cast<CCompositeObject *>(CCompositeObjectManager::Instance()->GetObjectByID(cloneID));
Dbg_MsgAssert( pObject, ( "Couldn't find object id %d to clone", cloneID ) );
CModelComponent *p_src_model_comp = GetModelComponentFromObject(pObject);
Dbg_MsgAssert( p_src_model_comp, ( "Couldn't find model component in object id %d", cloneID ) );
Dbg_MsgAssert( p_src_model_comp->mp_model, ( "Couldn't find CModel in model component of object id %d", cloneID ) );
uint32 geomName;
Script::CArray *p_geom_array=NULL;
if ( pParams->GetChecksum( CRCD(0xe7308c1f,"geom"), &geomName, Script::NO_ASSERT ) )
{
Nx::CGeom *p_orig_geom = p_src_model_comp->mp_model->GetGeom(geomName);
Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom %s", Script::FindChecksumName(geomName)));
Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, mp_model);
Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(geomName)));
p_new_geom->SetActive(true);
mp_model->AddGeom(p_new_geom, geomName);
}
else if (pParams->GetArray(CRCD(0x44e31dff,"geoms"), &p_geom_array))
{
uint32 geomName;
for (uint i = 0; i < p_geom_array->GetSize(); i++)
{
geomName = p_geom_array->GetChecksum(i);
if (geomName)
{
Nx::CGeom *p_orig_geom = p_src_model_comp->GetModel()->GetGeom(geomName);
Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom %s", Script::FindChecksumName(geomName)));
Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, mp_model);
Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(geomName)));
p_new_geom->SetActive(true);
mp_model->AddGeom(p_new_geom, geomName);
}
}
}
}
else if ( pParams->GetText( CRCD(0x286a8d26,"model"), &pModelName, Script::NO_ASSERT )
|| pParams->GetText( CRCD(0xb974f2fc,"modelName"), &pModelName, Script::NO_ASSERT ) )
{
// TODO: If the model name is "none", then we shouldn't
// have added the modelcomponent in the first place...
// we should have some kind of higher-level logic decide
// whether or not to create the modelcomponent, based on
// whether the node specifies: model="none".
if ( Script::GenerateCRC(pModelName) == CRCD(0x806fff30,"none") )
{
; // do nothing
}
else
{
char fullModelName[vMAX_PATH];
uint32 skeletonName;
if ( pParams->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonName, Script::NO_ASSERT ) )
{
// if it's a skinned model
Gfx::GetModelFileName(pModelName, ".skin", fullModelName);
}
else
{
// if it's a nonskinned model
Gfx::GetModelFileName(pModelName, ".mdl", fullModelName);
}
Str::LowerCase( fullModelName );
bool forceTexDictLookup = pParams->ContainsFlag( CRCD(0x6c37fdc7,"AllowReplaceTex") );
// TODO: remove this for thps5 (it's only used for the boards
// in the skateshop, which need to do texture replacement, and
// thus cannot use the asset manager...)
if ( strstr( fullModelName, "thps4board_" ) )
{
useAssetManager = false;
}
// Model file name should look like this: "models/testcar/testcar.mdl"
Dbg_MsgAssert( strlen(fullModelName) < vMAX_PATH, ( "String too long" ) );
mp_model->AddGeom( fullModelName, 0, useAssetManager, texDictOffset, forceTexDictLookup );
#ifdef __PLAT_NGPS__
int lodIndex = 0;
// now load up the other LODs, if they exist
for ( int i = 0; i < vNUM_LODS; i++ )
{
char paramName[256];
sprintf( paramName, "modelLOD%d", lodIndex + 1 );
if ( pParams->GetText( paramName, &pModelName, Script::NO_ASSERT ) )
{
float modelDist;
sprintf( paramName, "modelLODdist%d", lodIndex + 1 );
if ( pParams->GetFloat( paramName, &modelDist, Script::NO_ASSERT ) )
{
uint32 skeletonName;
if ( pParams->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonName, Script::NO_ASSERT ) )
{
// if it's a skinned model
Gfx::GetModelFileName(pModelName, ".skin", fullModelName);
}
else
{
// if it's a nonskinned model
Gfx::GetModelFileName(pModelName, ".mdl", fullModelName);
}
Str::LowerCase( fullModelName );
mp_model->AddGeom( fullModelName, lodIndex + 1, useAssetManager, texDictOffset );
enable_lod( lodIndex + 1, modelDist );
lodIndex++;
}
else
{
Dbg_MsgAssert( 0, ( "Expected to find parameter for LOD model dist: %s", paramName ) );
}
}
}
#endif
}
}
else if ( pParams->GetChecksum( CRCD(0x286a8d26,"model"), &model_name_checksum, Script::NO_ASSERT ))
{
Nx::CSector *p_source_sector = Nx::CEngine::sGetSector(model_name_checksum);
Dbg_MsgAssert(p_source_sector,("Could not find sector %s\n",Script::FindChecksumName(model_name_checksum)));
Nx::CGeom *p_orig_geom = p_source_sector->GetGeom();
Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom for %s",Script::FindChecksumName(model_name_checksum)));
Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, (Nx::CScene*)NULL);
Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(model_name_checksum)));
p_new_geom->SetActive(true);
mp_model->AddGeom(p_new_geom, 0);
}
}
// update its position
if ( GetObject()->IsFinalized() )
{
FinalizeModelInitialization();
}
// now that the model has been created,
// remember the bounding sphere
m_original_bounding_sphere = mp_model->GetBoundingSphere();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// create-a-ped, create-a-skater
void CModelComponent::InitModelFromProfile( Gfx::CModelAppearance* pAppearance, bool useAssetManager, uint32 texDictOffset, uint32 buildScript )
{
Dbg_MsgAssert( mp_model, ( "No model?" ) );
Nx::CModel* pModel = mp_model;
// GJ: shouldn't destroy the existing model
// it's up to each build script to do this...
// (in case we just want to re-apply a few
// operations)
// pModel->ClearGeoms();
Gfx::CSkeleton* pSkeleton = NULL;
Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( GetObject() );
if ( pSkeletonComponent )
{
pSkeleton = pSkeletonComponent->GetSkeleton();
}
Gfx::CModelBuilder theBuilder( useAssetManager, texDictOffset );
if ( buildScript )
{
theBuilder.BuildModel( pAppearance, pModel, pSkeleton, buildScript );
}
else
{
theBuilder.BuildModel( pAppearance, pModel, pSkeleton );
}
// update its position
if ( GetObject()->IsFinalized() )
{
FinalizeModelInitialization();
}
// now that the model has been created,
// remember the bounding sphere
m_original_bounding_sphere = pModel->GetBoundingSphere();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CModelComponent::enable_lod(uint32 componentName, float distance)
{
if ( m_numLODs >= vNUM_LODS )
{
Dbg_MsgAssert( 0, ( "Too many LODs!" ) );
return false;
}
Nx::CGeom* pGeom = mp_model->GetGeom( componentName );
if ( !pGeom )
{
Dbg_MsgAssert( 0, ( "Couldn't find geom named %s", Script::FindChecksumName(componentName) ) );
return false;
}
// printf( "Enabled lod %08x\n", componentName );
// GJ TODO: should sort the lods by distance,
// and check for duplicates!
m_LODdist[m_numLODs] = distance;
m_numLODs++;
// GJ TODO: each client needs to do this
// on a case-by-case basis...
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CModelComponent::HideGeom( uint32 geomName, bool hidden, bool propagate )
{
if( propagate )
{
GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
GameNet::PlayerInfo* player;
player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
if( player && player->IsLocalPlayer())
{
Net::Client* client;
GameNet::MsgHideAtomic msg;
Net::MsgDesc msg_desc;
client = gamenet_man->GetClient( player->GetSkaterNumber() );
Dbg_Assert( client );
//msg.m_Time = client->m_Timestamp;
msg.m_Hide = hidden;
msg.m_AtomicName = geomName;
msg.m_ObjId = GetObject()->GetID();
msg_desc.m_Data = &msg;
msg_desc.m_Length = sizeof( GameNet::MsgHideAtomic );
msg_desc.m_Id = GameNet::MSG_ID_SET_HIDE_ATOMIC;
client->EnqueueMessageToServer( &msg_desc );
}
}
if ( mp_model )
{
mp_model->HideGeom( geomName, hidden );
}
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CModelComponent::GeomHidden( uint32 geomName )
{
return (mp_model->GeomHidden( geomName ));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::GetDisplayMatrixWithExtraRotation( Mth::Matrix& displayMatrix )
{
CCompositeObject* pObject = GetObject();
Dbg_MsgAssert( pObject, ( "Couldn't find parent object" ) );
displayMatrix = pObject->GetDisplayMatrix();
displayMatrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
if (mFlipDisplayMatrix)
{
displayMatrix[Z] = -displayMatrix[Z];
displayMatrix[X] = -displayMatrix[X];
}
// Record the display matrix for use by the camera before applying any extra rotation.
// m_camera_display_matrix=m_display_matrix;
Mth::Matrix extra_display_rotation;
// only do the math if we're not dealing w/ the identity...
bool is_identity = true;
float new_angle;
if (mpDisplayRotationInfo[0].m_active)
{
new_angle=mpDisplayRotationInfo[0].CalculateNewAngle();
if ( new_angle )
{
if (is_identity)
{
extra_display_rotation.Ident();
}
is_identity = false;
extra_display_rotation.RotateX(Mth::DegToRad(new_angle));
}
}
if (mpDisplayRotationInfo[1].m_active)
{
new_angle=mpDisplayRotationInfo[1].CalculateNewAngle();
if ( new_angle )
{
if (is_identity)
{
extra_display_rotation.Ident();
}
is_identity = false;
extra_display_rotation.RotateY(Mth::DegToRad(new_angle));
}
}
if (mpDisplayRotationInfo[2].m_active)
{
new_angle=mpDisplayRotationInfo[2].CalculateNewAngle();
if ( new_angle )
{
if (is_identity)
{
extra_display_rotation.Ident();
}
is_identity = false;
extra_display_rotation.RotateZ(Mth::DegToRad(new_angle));
}
}
Mth::Vector off(0.0f, 0.0f, 0.0f, 0.0f);
if ( !is_identity )
{
off=m_display_rotation_offset;
if ( !( off[X] == 0.0f && off[Y] == 0.0f && off[Z] == 0.0f ) )
{
// is_identity = false;
off=off-off*extra_display_rotation; // Would zero it if it's the identity
off=off*displayMatrix;
}
// if ( !is_identity )
{
displayMatrix=extra_display_rotation*displayMatrix;
}
}
//#ifdef DEBUG_DISPLAY_MATRIX
// dodgy_test(); printf("%d: Setting display_matrix[Y][Y] to %f, [X][X] to %f\n",__LINE__,m_display_matrix[Y][Y],m_display_matrix[X][X]);
//#endif
displayMatrix[Mth::POS] = GetObject()->GetPos();
displayMatrix[Mth::POS] += off;
displayMatrix[Mth::POS] += mDisplayOffset;
displayMatrix[Mth::POS][W] = 1.0f;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::Hide( bool shouldHide )
{
if ( mp_model )
{
mp_model->Hide( shouldHide );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::Teleport()
{
Dbg_MsgAssert( GetObject()->IsFinalized(), ( "Teleporting unfinalized component!" ) );
FinalizeModelInitialization();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::Update()
{
if ( m_hasRefObject )
{
#ifdef __NOPT_ASSERT__
Obj::CMovieManager* pMovieManager = Mdl::Skate::Instance()->GetMovieManager();
Dbg_MsgAssert( !pMovieManager->IsRolling(), ( "Wasn't expecting cutscene to be rolling" ) );
#endif
// this code was lifted from cutscenedetails.cpp,
// which is why it has all these weird temp
// variables referencing its own member vars.
// it's used for doing the create-a-trick skater
Obj::CModelComponent* pModelComponent = this;
Obj::CCompositeObject* pCompositeObject = GetObject();
uint32 refObjectName = pModelComponent->GetRefObjectName();
Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
Dbg_Assert( pRefObject );
Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject( pRefObject );
Dbg_Assert( pRefModelComponent );
Nx::CModel* pRefModel = pRefModelComponent->GetModel();
bool should_animate = true;
Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
Dbg_MsgAssert( pSkeletonComponent, ( "Was expecting a skeleton component" ) );
Dbg_Assert( pSkeletonComponent == mp_skeleton_component );
Mth::Matrix theDisplayMatrix;
this->GetDisplayMatrixWithExtraRotation( theDisplayMatrix );
// the return matrix already takes the position (plus an additional offset)
// into account, so we don't have to do the following:
// theDisplayMatrix[Mth::POS] = GetObject()->GetPos();
pRefModel->Render( &theDisplayMatrix, !should_animate, pSkeletonComponent->GetSkeleton() );
pRefModel->SetBoneMatrixData( pSkeletonComponent->GetSkeleton() );
}
// Mick: Don't need to update it if not active, just leave it where it is
else if (mp_model && mp_model->GetActive())
{
Dbg_MsgAssert(GetObject()->IsFinalized(),("Update() to UnFinalized Composite object %s",Script::FindChecksumName(GetObject()->GetID())));
Mth::Matrix theDisplayMatrix;
GetDisplayMatrixWithExtraRotation( theDisplayMatrix );
// theDisplayMatrix = GetObject()->GetDisplayMatrix();
// theDisplayMatrix[Mth::POS] = GetObject()->GetPos();
// theDisplayMatrix[Mth::POS][W] = 1.0f;
// TODO: The interface between different components
// should be more generic, maybe...
Gfx::CSkeleton* pSkeleton = NULL;
if ( mp_skeleton_component )
{
pSkeleton = mp_skeleton_component->GetSkeleton();
}
// default to true, for skeletal cars...
bool should_animate = true;
if ( mp_animation_component )
{
// either the animation component should cache this
// data rather than doing the visibility test twice,
// or it should be able to set some member inside
// the model component (CModelComponent::MarkAnimationAsDirty?)
should_animate = mp_animation_component->ShouldAnimate();
}
#ifdef __PLAT_NGPS__
// if it has LODs, then hide all the unnecessary ones
if ( m_numLODs > 0 )
{
// first hide all the models, including the base one (0)
for ( int i = 0; i < m_numLODs + 1; i++ )
{
mp_model->HideGeom( i, true );
}
// now go through and unhide the correct one
float distanceSqrToCamera = mp_suspend_component->GetDistanceSquaredToCamera();
// printf( "distance to camera: %f feet\n", sqrtf(distanceSqrToCamera)/12.0f );
bool found = false;
for ( int i = 0; i < m_numLODs; i++ )
{
if ( distanceSqrToCamera < ( m_LODdist[i] * m_LODdist[i] ) )
{
mp_model->HideGeom( i, false );
found = true;
}
}
if ( !found )
{
// then the last lod is active
mp_model->HideGeom( m_numLODs, false );
}
}
#endif
// TODO: if it's offscreen, the data shouldn't be copied over either...
mp_model->Render( &theDisplayMatrix, !should_animate, pSkeleton );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::Finalize()
{
mp_skeleton_component = GetSkeletonComponentFromObject( GetObject() );
mp_animation_component = GetAnimationComponentFromObject( GetObject() );
mp_suspend_component = GetSuspendComponentFromObject( GetObject() );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::SetModelLODDistance( int lodIndex, float distance )
{
if ( !mp_model )
{
Dbg_Message( "No model!" );
return;
}
if ( m_numLODs == 0 )
{
Dbg_Message( "Model %s has no lods", Script::FindChecksumName(GetObject()->GetID()) );
}
if ( lodIndex < 1 || lodIndex > m_numLODs )
{
Dbg_Message( "Was expecting an lod index between 1 and %d", m_numLODs);
return;
}
m_LODdist[lodIndex-1] = distance;
Dbg_Message( "ModelLODDist%d is now %f", lodIndex, distance );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::SetBoundingSphere( const Mth::Vector& newSphere )
{
if ( mp_model )
{
mp_model->SetBoundingSphere( newSphere );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::UpdateBrightness()
{
Dbg_MsgAssert(mp_model, ("UpdateBrightness: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("UpdateBrightness: MovingObject has no model lights"));
mp_model->GetModelLights()->UpdateBrightness();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CModelComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
bool success = true;
Script::CStruct *p_script_params;
p_script_params = NULL;
if( pScript )
{
p_script_params = pScript->GetParams();
}
switch (Checksum)
{
// @script | Obj_EnableScaling | in case you want to dynamically
// turn on scaling
case ( 0x347ff11b ): // Obj_EnableScaling
if ( mp_model )
{
mp_model->EnableScaling( true );
}
break;
// @script | Obj_DisableScaling | in case you want to dynamically
// turn off scaling
case ( 0xf951aa64 ): // Obj_DisableScaling
if ( mp_model )
{
mp_model->EnableScaling( false );
}
break;
// @script | Obj_ApplyScaling | scale on x,y,z
// @parm float | x |
// @parm float | y |
// @parm float | z |
case ( 0x657ff1ed ): // Obj_ApplyScaling
{
// GJ: Scaling only refers to the renderable
// model, not the collision volume...
if ( mp_model )
{
Mth::Vector theScale(1.0f,1.0f,1.0f);
if ( Gfx::GetScaleFromParams( &theScale, pParams ) )
{
mp_model->SetScale( theScale );
mp_model->EnableScaling( true );
}
}
}
break;
// @script | Obj_ClearColor | turns off color modulation on this resetting it to the default color
case ( 0x6052480a ): // Obj_ClearColor
{
if ( mp_model )
{
mp_model->ClearColor( CRCD(0xc4e78e22,"all") );
}
}
break;
// @script | Obj_SetColor |
// @parm int | h | hue - between 0 and 360
// @parm int | s | saturation - between 0 and 100
// @parm int | v | value - between 0 and 100
case ( 0x76ecfa1c ): // Obj_SetColor
{
if ( mp_model )
{
int h, s, v;
if ( pParams->GetInteger( CRCD(0x6e94f918,"h"), &h, false )
&& pParams->GetInteger( CRCD(0xe4f130f4,"s"), &s, false )
&& pParams->GetInteger( CRCD(0x949bc47b,"v"), &v, false ) )
{
mp_model->ModulateColor( CRCD(0xc4e78e22,"all"), (float)h, (float)s / 100.0f, (float)v / 100.0f );
}
}
}
break;
// @script | Obj_ReplaceTexture | Replaces a texture in the model's dictionary
// @parm text | src | filename of the source texture
// @parm text | dest | filename of the destination texture
// @parmopt name | in | all | name of geom in which to look for texture (defaults to global replacement)
// Ex: Obj_ReplaceTexture src="knee_l.png" dest="textures/skater_m/knee_scuff" in=skater_m_legs
case 0x83f9be15: // Obj_ReplaceTexture
{
const char* pSrcTexture;
const char* pDstTexture;
success = false;
Script::CArray* pArray;
if ( pParams->GetArray( CRCD(0x5ef31148,"array"), &pArray, Script::NO_ASSERT ) )
{
for ( uint32 i = 0; i < pArray->GetSize(); i++ )
{
Script::CStruct* pSubParams = pArray->GetStructure( i );
pSubParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
pSubParams->GetText( CRCD(0x7799d66c,"dest"), &pDstTexture, Script::ASSERT );
// by default, it searches globally in the model for the correct texture
uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
pSubParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
if ( mp_model )
{
if ( mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDstTexture ) )
{
success = true;
}
}
}
}
else
{
pParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
pParams->GetText( CRCD(0x7799d66c,"dest"), &pDstTexture, Script::ASSERT );
// by default, it searches globally in the model for the correct texture
uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
pParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
if ( mp_model )
{
if ( mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDstTexture ) )
{
success = true;
}
}
}
}
break;
// @script | Obj_ReplaceSpriteTexture | Replaces a texture in the model's dictionary
// @parm text | src | filename of the source texture
// @parm name | dest | name of the destination texture
// @parmopt name | in | all | name of geom in which to look for texture (defaults to global replacement)
// Ex: Obj_ReplaceSpriteTexture src="knee_l.png" dest=knee_scuff in=skater_m_legs
case 0x9a478afe: // Obj_ReplaceSpriteTexture
{
const char* pSrcTexture;
uint32 dest_checksum = 0;
pParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
pParams->GetChecksum( CRCD(0x7799d66c,"dest"), &dest_checksum, Script::ASSERT );
// by default, it searches globally in the model for the correct texture
uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
pParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
if ( mp_model )
{
success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, dest_checksum );
}
else
{
success = false;
}
}
break;
// @script | Obj_ClearGeoms | removes the model's geoms
case 0xbd18e2e3: // Obj_ClearGeoms
if ( mp_model )
{
mp_model->ClearGeoms();
}
break;
// @script | Obj_HasModelLights | tests existence of model lights
case 0xe11f85e8: // Obj_HasModelLights
{
Dbg_MsgAssert(mp_model, ("Obj_HasModelLights: CModel is NULL"));
return mp_model->GetModelLights() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
}
break;
// @script | Obj_UpdateBrightness | the normal lighting code assumes
// that the collision code will be run once a frame to copy over
// the m_ambient_base_color into the m_ambient_mod_color. unfortunately,
// the cutscene objects' ref objects don't go through this code, so I had to
// add this function to explicitly copy over the data... there's probably
// a cleaner way to do it, but I didn't want to break the existing code.
case 0x1c16332e: // Obj_UpdateBrightness
Dbg_MsgAssert(mp_model, ("Obj_UpdateBrightness: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_UpdateBrightness: MovingObject has no model lights"));
mp_model->GetModelLights()->UpdateBrightness();
break;
// @script | Obj_EnableAmbientLight | enables the ambient model light
case 0xa14d6372: // Obj_EnableAmbientLight
Dbg_MsgAssert(mp_model, ("Obj_EnableAmbientLight: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_EnableAmbientLight: MovingObject has no model lights"));
mp_model->GetModelLights()->EnableAmbientLight(true);
break;
// @script | Obj_DisableAmbientLight | disables the ambient model light
case 0x8a445e26: // Obj_DisableAmbientLight
Dbg_MsgAssert(mp_model, ("Obj_DisableAmbientLight: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_DisableAmbientLight: MovingObject has no model lights"));
mp_model->GetModelLights()->EnableAmbientLight(false);
break;
// @script | Obj_EnableDiffuseLight | enables a diffuse model light
// @parm int | index | Light number
case 0xed5d1550: // Obj_EnableDiffuseLight
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Obj_EnableDiffuseLight: Can't find 'index' of light"));
}
Dbg_MsgAssert(mp_model, ("Obj_EnableDiffuseLight: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_EnableDiffuseLight: MovingObject has no model lights"));
mp_model->GetModelLights()->EnableDiffuseLight(index, true);
break;
}
// @script | Obj_DisableDiffuseLight | disables a diffuse model light
// @parm int | index | Light number
case 0xc6542804: // Obj_DisableDiffuseLight
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Obj_DisableDiffuseLight: Can't find 'index' of light"));
}
Dbg_MsgAssert(mp_model, ("Obj_DisableDiffuseLight: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_DisableDiffuseLight: MovingObject has no model lights"));
mp_model->GetModelLights()->EnableDiffuseLight(index, false);
break;
}
// @script | Obj_SetLightAmbientColor | Sets the model light ambient color
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
case 0x3cf63049: // Obj_SetLightAmbientColor
{
int r, g, b;
if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
{
Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'r' color"));
}
if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
{
Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'g' color"));
}
if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
{
Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'b' color"));
}
Image::RGBA rgb(r, g, b, 0x80);
Dbg_MsgAssert(mp_model, ("Obj_SetLightAmbientColor: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightAmbientColor: MovingObject has no model lights"));
mp_model->GetModelLights()->SetLightAmbientColor(rgb);
mp_model->GetModelLights()->EnableAmbientLight(true);
break;
}
// @script | Obj_SetLightDirection | Sets the unit direction vector of a model light
// @parm int | index | Light number
// @parm vector | direction | Unit direction vector (overrides heading and pitch)
// @parm float | heading | Heading angle (in degrees)
// @parm float | pitch | Pitch angle (in degrees)
case 0x46a1f4a4: // Obj_SetLightDirection
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Obj_SetLightDirection: Can't find 'index' of light"));
}
float heading, pitch;
Mth::Vector direction(0, 0, 1, 0);
if (pParams->GetVector(CRCD(0xc1b52e4c,"direction"), &direction))
{
direction[W] = 0.0f; // This is the only way to force this to be a vector (as opposed to a point)
//Dbg_Message("************ direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
}
else if (pParams->GetFloat(CRCD(0xfd4bc03e,"heading"), &heading) && pParams->GetFloat(CRCD(0xd8604126,"pitch"), &pitch))
{
direction.RotateX(Mth::DegToRad(pitch));
direction.RotateY(Mth::DegToRad(heading));
//Dbg_Message("************ heading and pitch direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
}
else
{
Dbg_MsgAssert(0, ("Obj_SetLightDirection: Can't find 'direction' or 'heading' and 'pitch' of light"));
}
Dbg_MsgAssert(mp_model, ("Obj_SetLightDirection: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightDirection: MovingObject has no model lights"));
mp_model->GetModelLights()->SetLightDirection(index, direction);
break;
}
// @script | Obj_SetLightDiffuseColor | Sets a model light diffuse color
// @parm int | index | Light number
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
case 0x70e6466b: // Obj_SetLightDiffuseColor
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Obj_SetLightDiffuseLight: Can't find 'index' of light"));
}
int r, g, b;
if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
{
Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'r' color"));
}
if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
{
Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'g' color"));
}
if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
{
Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'b' color"));
}
Image::RGBA rgb(r, g, b, 0x80);
Dbg_MsgAssert(mp_model, ("Obj_SetLightDiffuseColor: CModel is NULL"));
Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightDiffuseColor: MovingObject has no model lights"));
mp_model->GetModelLights()->SetLightDiffuseColor(index, rgb);
mp_model->GetModelLights()->EnableDiffuseLight(index, true);
break;
}
// @script | Obj_SetUVOffset | Sets the UV offset of a material pass
// @parm name | material | material name
// @parm int | pass | material pass
// @parm float | u | u offset
// @parm float | v | v offset
case 0x57eb306e: // Obj_SetUVOffset
{
uint32 mat_checksum;
if (!pParams->GetChecksum(CRCD(0x83418a6a,"material"), &mat_checksum))
{
Dbg_MsgAssert(0, ("Can't find parameter material"));
}
// Extract all the parameters
int pass = 0;
float u_offset;
float v_offset;
bool found_all;
found_all = pParams->GetFloat(CRCD(0xd9295c1,"u"), &u_offset);
found_all &= pParams->GetFloat(CRCD(0x949bc47b,"v"), &v_offset);
found_all &= pParams->GetInteger(CRCD(0x318f2bdb,"pass"), &pass);
Dbg_MsgAssert(found_all, ("Missing one or more of the wibble offset parameters. Must fill them all out."));
Dbg_MsgAssert(mp_model, ("Obj_SetUVOffset: CModel is NULL"));
mp_model->SetUVOffset(mat_checksum, pass, u_offset, v_offset);
break;
}
// @script | Obj_SetUVParams | Sets the UV parameters of a material pass
// @parm name | material | material name
// @parm int | pass | material pass
// @parmopt float | uoff | 0.0 | u offset
// @parmopt float | voff | 0.0 | v offset
// @parmopt float | uscale | 1.0 | u scale
// @parmopt float | vscale | 1.0 | v scale
// @parmopt float | rot | 0.0 | rotation in degrees
case 0x812ff44d: // Obj_SetUVParams
{
uint32 mat_checksum;
int pass;
if (!pParams->GetChecksum(CRCD(0x83418a6a,"material"), &mat_checksum))
{
Dbg_MsgAssert(0, ("Can't find parameter material"));
}
if (!pParams->GetInteger(CRCD(0x318f2bdb,"pass"), &pass))
{
Dbg_MsgAssert(0, ("Can't find parameter pass"));
}
// Clear matrix
Mth::Matrix mat;
mat.Ident();
// Scaling
float uscale = 1.0f;
float vscale = 1.0f;
bool scale = pParams->GetFloat(CRCD(0x3fb610b7,"uscale"), &uscale);
scale = pParams->GetFloat(CRCD(0xb9226219,"vscale"), &vscale) || scale;
if (scale)
{
Dbg_MsgAssert((uscale >= 0.0f) && (vscale >= 0.0f), ("Obj_SetUVParams: Don't use negative scales."));
mat[0][0] *= uscale;
mat[1][1] *= vscale;
}
// Rotation
float rot_deg;
if (pParams->GetFloat(CRCD(0xe2c6589e,"rot"), &rot_deg))
{
mat.RotateZ(Mth::DegToRad(rot_deg));
}
// Offset
float uoffset = 0.0f;
float voffset = 0.0f;
bool translate = pParams->GetFloat(CRCD(0x56a7f41c,"uoff"), &uoffset);
translate = pParams->GetFloat(CRCD(0x44125bf2,"voff"), &voffset) || translate;
if (translate)
{
mat[3][0] = uoffset;
mat[3][1] = voffset;
}
Dbg_MsgAssert(mp_model, ("Obj_SetUVParams: CModel is NULL"));
mp_model->SetUVMatrix(mat_checksum, pass, mat);
break;
}
// @script | EnableDisplayFlip |
case 0x721184c7: // EnableDisplayFlip
mFlipDisplayMatrix=true;
break;
// @script | DisableDisplayFlip |
case 0x4af3b1f7: // DisableDisplayFlip
mFlipDisplayMatrix=false;
break;
// @script | Obj_InitModel |
case 0x4d8e11ab: // Obj_InitModel
InitModel( pParams );
break;
// @script | SwitchOffAtomic |
// @uparm name | geom name
case 0xe48fd084: // SwitchOffAtomic
{
uint32 atomicName;
pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
HideGeom(atomicName, true, true);
}
break;
// @script | SwitchOnAtomic |
// @uparm name | geom name
case 0x07d0c128: // SwitchOnAtomic
{
uint32 atomicName;
pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
HideGeom(atomicName, false, true);
}
break;
// @script | AtomicIsHidden |
// @uparm name | geom name
case 0xe7fa7dd0: // AtomicIsHidden
{
uint32 atomicName;
pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
if (GeomHidden( atomicName ))
pScript->GetParams()->AddInteger(CRCD(0x77a21642,"hidden"), 1);
else
pScript->GetParams()->AddInteger(CRCD(0x77a21642,"hidden"), 0);
}
break;
// @script | Obj_InitModelFromProfile | used for loading up peds and re-initializing the preview model
case 0x98ed5c8b: // Obj_InitModelFromProfile
{
if ( mp_model )
{
int texDictOffset = 0;
pParams->GetInteger( CRCD(0xf891ac27,"texDictOffset"), &texDictOffset, Script::NO_ASSERT );
int useAssetManager = 0;
pParams->GetInteger( CRCD(0xc63c8d38,"use_asset_manager"), &useAssetManager, Script::NO_ASSERT );
Script::CStruct* pAppearanceParams;
pParams->GetStructure( CRCD(0x456d28d1,"struct"), &pAppearanceParams, Script::ASSERT );
uint32 buildScript = 0;
pParams->GetChecksum( CRCD(0x299ee352,"buildscript"), &buildScript, Script::NO_ASSERT );
// load the skeleton (do i really need to do a sanity check here?)
// Dbg_MsgAssert( GetSkeleton(), ( "Object has no skeleton" ) )
Gfx::CModelAppearance theAppearance;
theAppearance.Load( pAppearanceParams, true );
InitModelFromProfile( &theAppearance, useAssetManager, texDictOffset, buildScript );
}
}
break;
// @script | Obj_SetBoundingSphere | sets a sphere with the specified radius (assumes position is 0,0,0)
// @uparm float | radius of bounding sphere
case 0x452fb315: // Obj_SetBoundingSphere
{
float radius;
pParams->GetFloat( NONAME, &radius, Script::ASSERT );
// take the existing position, and change the radius on it
Mth::Vector newSphere( 0.0f, 0.0f, 0.0f, radius );
SetBoundingSphere( newSphere );
}
break;
// @script | Obj_RestoreBoundingSphere | resets the sphere to whatever it was after the model was initialized
case 0x5b41da8f: // Obj_RestoreBoundingSphere
{
SetBoundingSphere( m_original_bounding_sphere );
}
break;
// @script | RotateDisplay |
// @parmopt float | Duration | 0.0 | Duration time (default is ms) The rotation will take this much time to complete.
// @flag seconds | Units of seconds
// @flag frames | Units of frames
// @flag x | Rotate about the x-axis
// @flag y | Rotate about the y-axis
// @flag z | Rotate about the z-axis
// @parmopt float | StartAngle | 0.0 | Start angle, in degrees. The skater will instantly pop to this angle
// when the RotateDisplay command is issued.
// @parmopt float | EndAngle | 360.0 | End angle, in degrees. The skater will rotate from
// the start angle to the end angle over the time period specified by Duration.
// @parmopt int | SinePower | 0 | If set to zero (the default) the angle will change at a constant speed over
// the specified duration.
// A SinePower of 1 makes the speed start instantly but slow down smoothly at the end.
// A SinePower of greater than one will make the speed smoothly accelerate from zero to a maximum, then
// smoothly decelerate to zero. Values of 2,3 or 4 seem to work best.
// A SinePower of -1 will make the speed smoothly accelerate from zero to a constant value. It will not
// decelerate at the end.
// Note that the final constant speed will be 1.5707 times the constant speed that would be have been used
// if the SinePower was 0. The speed has to be a bit higher to make up for the smooth acceleration at the start
// whilst keeping within the same Duration value.
// So, if you want to follow with another RotateDisplay that maintains that constant speed, you will need to
// divide it's duration by 1.5707, for example:
// RotateDisplay y Duration=3 seconds EndAngle=(360*4) SinePower=-1
// Wait 3 seconds
// RotateDisplay y Duration=(3/1.5707) seconds EndAngle=(360*4) SinePower=0
// @parmopt vector | RotationOffset | (0,30,0) | The offset of the point about which to rotate the skater.
// @flag HoldOnLastAngle | If specified then the rotation will not pop back to what it was at
// the end of the duration, but will stick until a CancelRotateDisplay command is issued.
case 0xa4c25c2f: // RotateDisplay
{
#ifdef __USER_DAN__
printf("RotateDisplay\n");
Script::PrintContents(pParams);
#endif
float duration = 0.0f;
pParams->GetFloat(CRCD(0x79a07f3f, "Duration"), &duration);
if (pParams->ContainsFlag(CRCD(0xd029f619, "Seconds")) || pParams->ContainsFlag(CRCD(0x49e0ee96, "Second")))
{
// Convert from seconds to milliseconds
duration *= 1000.0f;
}
else if (pParams->ContainsFlag(CRCD(0x19176c5, "Frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "Frame")))
{
// Convert from frames to milliseconds
duration = duration * 1000.0f / 60.0f;
}
else
{
// Milliseconds is what we want, so nothing to do.
}
float start_angle = 0.0f;
pParams->GetFloat(CRCD(0x9de2a72f, "StartAngle"), &start_angle);
float end_angle = 0.0f;
pParams->GetFloat(CRCD(0x8c9946a9, "EndAngle"), &end_angle);
int sine_power = 0;
pParams->GetInteger(CRCD(0xf164f03d, "SinePower"), &sine_power);
m_display_rotation_offset.Set(0.0f, 30.0f, 0.0f);
pParams->GetVector(CRCD(0x8f38f109, "RotationOffset"), &m_display_rotation_offset);
bool hold_on_last_angle = pParams->ContainsFlag(CRCD(0xe14bc9ac, "HoldOnLastAngle"));
Tmr::Time start_time = Tmr::ElapsedTime(0);
char flags = 0;
if (pParams->ContainsFlag(CRCD(0x7323e97c, "X")))
{
flags |= 1;
mpDisplayRotationInfo[0].SetUp(duration,
start_time,
start_angle,
end_angle - start_angle,
sine_power,
hold_on_last_angle);
}
if (pParams->ContainsFlag(CRCD(0x424d9ea, "Y")))
{
flags |= 2;
mpDisplayRotationInfo[1].SetUp(duration,
start_time,
start_angle,
end_angle - start_angle,
sine_power,
hold_on_last_angle);
}
if (pParams->ContainsFlag(CRCD(0x9d2d8850, "Z")))
{
flags |= 4;
mpDisplayRotationInfo[2].SetUp(duration,
start_time,
start_angle,
end_angle - start_angle,
sine_power,
hold_on_last_angle);
}
// if our object is associated with a local player, send a MsgRotateDisplay message
GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
GameNet::PlayerInfo* player = gamenet_man->GetPlayerByObjectID(GetObject()->GetID());
if (player && player->IsLocalPlayer())
{
Net::Client* client;
GameNet::MsgRotateDisplay msg;
Net::MsgDesc msg_desc;
client = gamenet_man->GetClient( player->GetSkaterNumber() );
Dbg_Assert( client );
//msg.m_Time = client->m_Timestamp;
msg.m_Duration = static_cast< int >( duration );
msg.m_StartAngle = static_cast< short >( start_angle );
msg.m_DeltaAngle = static_cast< short >( end_angle - start_angle );
msg.m_SinePower = static_cast< int >( sine_power );
msg.m_ObjId = GetObject()->GetID();
msg.m_HoldOnLastAngle = hold_on_last_angle;
msg.m_Flags = flags;
msg_desc.m_Data = &msg;
msg_desc.m_Length = sizeof( GameNet::MsgRotateDisplay );
msg_desc.m_Id = GameNet::MSG_ID_ROTATE_DISPLAY;
client->EnqueueMessageToServer( &msg_desc );
}
}
break;
// @script | CancelRotateDisplay | Instantly cancels any rotation due to a RotateDisplay
// command.
case 0x4424c267: // CancelRotateDisplay
{
mpDisplayRotationInfo[0].Clear();
mpDisplayRotationInfo[1].Clear();
mpDisplayRotationInfo[2].Clear();
// if our object is associated with a local player, send a MsgRotateDisplay message
GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
GameNet::PlayerInfo* player = gamenet_man->GetPlayerByObjectID(GetObject()->GetID());
if (player && player->IsLocalPlayer())
{
Net::Client* client;
GameNet::MsgObjMessage msg;
Net::MsgDesc msg_desc;
client = gamenet_man->GetClient( player->GetSkaterNumber() );
Dbg_Assert( client );
//msg.m_Time = client->m_Timestamp;
msg.m_ObjId = GetObject()->GetID();
msg_desc.m_Data = &msg;
msg_desc.m_Length = sizeof( GameNet::MsgObjMessage );
msg_desc.m_Id = GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY;
client->EnqueueMessageToServer( &msg_desc );
}
}
break;
default:
return CBaseComponent::MF_NOT_EXECUTED;
}
return success ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef __DEBUG_CODE__
Dbg_MsgAssert(p_info,("NULL p_info sent to CModelComponent::GetDebugInfo"));
// we call the base component's GetDebugInfo, so we can add info from the common base component
CBaseComponent::GetDebugInfo(p_info);
p_info->AddChecksum(CRCD(0xc3f4169a,"filename"),mp_model->GetFileName());
Mth::Vector scale=mp_model->GetScale();
p_info->AddVector(CRCD(0x13b9da7b,"scale"),scale.GetX(),scale.GetY(),scale.GetZ());
uint32 mode=CRCD(0x52d95838,"Unknown");
switch (mp_model->GetRenderMode())
{
case Nx::vTEXTURED: mode=CRCD(0xd939b2b6,"vTEXTURED"); break;
case Nx::vSKELETON: mode=CRCD(0x22b5274d,"vSKELETON"); break;
case Nx::vGOURAUD: mode=CRCD(0xe6d7f928,"vGOURAUD"); break;
case Nx::vFLAT: mode=CRCD(0x3b4168d5,"vFLAT"); break;
case Nx::vWIREFRAME: mode=CRCD(0xec50e3e1,"vWIREFRAME"); break;
case Nx::vBBOX: mode=CRCD(0x2988b32d,"vBBOX"); break;
case Nx::vNONE: mode=CRCD(0x119bc25e,"vNONE"); break;
default:
break;
}
p_info->AddChecksum(CRCD(0x2f006949,"RenderMode"),mode);
#endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Mick: This function actually gets called TWICE when a model is created
// Once from the call to Teleport which is called from the CCOmpositeObject::Teleport functions
// and then once from InitModelFromProfile(), whcih is called after the model is finalized.
// Only on the second call will the model have its geometry set up correctly.
// This does not seem to cause problems, but should probably be re-worked for future iterations of the
// engine.
void CModelComponent::FinalizeModelInitialization()
{
// need to synchronize rendered model's position to initial world position
Update();
if ( mp_model && mp_model->GetModelLights() )
{
UpdateBrightness();
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::RefreshModel( Gfx::CModelAppearance* pAppearance, uint32 buildScript )
{
if ( mp_model )
{
// it doesn't matter whether we use the asset manager
// or which texture dict offset we use, because
// the build script will only affect colors...
bool dummyUseAssetManager = true;
int dummyTexDictOffset = 0;
InitModelFromProfile( pAppearance, dummyUseAssetManager, dummyTexDictOffset, buildScript );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SDisplayRotationInfo::Clear()
{
SDisplayRotation *p_rotation=mpRotations;
for (int i=0; i<MAX_ROTATIONS; ++i)
{
p_rotation->Clear();
++p_rotation;
}
m_active = false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SDisplayRotationInfo::SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle)
{
SDisplayRotation *p_rotation=mpRotations;
for (int i=0; i<MAX_ROTATIONS; ++i)
{
if (!p_rotation->mDispRotating)
{
p_rotation->SetUp(duration, start_time, start_angle, change_in_angle, sine_power, holdOnLastAngle);
m_active = true;
return;
}
++p_rotation;
}
//Dbg_MsgAssert(0,("Too many rotations overlaid at once, maximum is %d",SDisplayRotationInfo::MAX_ROTATIONS));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
float SDisplayRotationInfo::CalculateNewAngle()
{
float new_angle=0.0f;
if (m_active)
{
m_active = false;
SDisplayRotation *p_rotation=mpRotations;
for (int i=0; i<MAX_ROTATIONS; ++i)
{
if (p_rotation->mDispRotating)
{
m_active = true;
new_angle+=p_rotation->CalculateNewAngle();
}
++p_rotation;
}
}
return new_angle;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SDisplayRotation::Clear()
{
mDispRotating=false;
mHoldOnLastAngle=false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SDisplayRotation::SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle)
{
mDispRotating=true;
mDispDuration=duration;
mDispStartTime=start_time;
mDispStartAngle=start_angle;
mDispChangeInAngle=change_in_angle;
mDispSinePower=sine_power;
mHoldOnLastAngle=holdOnLastAngle;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
float SDisplayRotation::CalculateNewAngle()
{
float new_angle=0.0f;
if (mDispRotating)
{
float t=Tmr::ElapsedTime(0)-mDispStartTime;
if (t>mDispDuration)
{
if (mHoldOnLastAngle)
{
// Stick forever on the end angle, until the mHoldOnLastAngle gets reset
// by a CancelRotateDisplay script command.
new_angle=mDispStartAngle+mDispChangeInAngle;
}
else
{
mDispRotating=false;
}
}
else
{
if (mDispSinePower)
{
float s;
if (mDispSinePower<0)
{
// If a negative sine power is specified, then use an upside-down and
// back-to-front sine wave. This gives a rotation which smoothly accelerates
// to a constant speed.
s=1.0f-sinf(1.570796327f-t*1.570796327f/mDispDuration);
}
else
{
s=sinf(t*1.570796327f/mDispDuration);
for (int i=0; i<mDispSinePower-1; ++i)
{
s=s*s;
}
}
new_angle=mDispStartAngle+s*mDispChangeInAngle;
}
else
{
new_angle=mDispStartAngle+t*mDispChangeInAngle/mDispDuration;
}
}
}
return new_angle;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
uint32 CModelComponent::GetRefObjectName()
{
Dbg_MsgAssert( m_hasRefObject, ( "Object %s doesn't have a ref object", Script::FindChecksumName(GetObject()->GetID()) ) );
return m_refObjectName;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::ApplyLightingFromCollision( CFeeler& feeler )
{
if (!feeler.IsBrightnessAvailable()) return;
if (Nx::CModelLights* p_model_lights = GetModel()->GetModelLights())
{
p_model_lights->SetBrightness(feeler.GetBrightness());
}
else
{
Nx::CLightManager::sSetBrightness(feeler.GetBrightness());
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CModelComponent::ApplySceneLighting( Image::RGBA color )
{
// Mick: if the model is a level object, then apply the scene lighthing to it
if (m_isLevelObject)
{
mp_model->GetGeomByIndex(0)->SetColor(color);
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
}
#if 0
/******************************************************************/
/* */
/* */
/******************************************************************/
// GJ TODO: these cheat codes are kind of kludgy
// and should be re-implemented later in a more
// component-friendly format.
bool apply_cheat_scale( Gfx::CSkeleton* pSkeleton, Nx::CModel* pModel, uint32 cheatName, uint32 bodyShapeName, uint32 animScriptName )
{
int flag = Script::GetInteger( cheatName, Script::ASSERT );
if ( Mdl::Skate::Instance()->GetCareer()->GetGlobalFlag(flag) )
{
Dbg_Assert( pModel );
if ( animScriptName == CRCD(0x501949bd,"animload_human")
|| animScriptName == CRCD(0x105f6aa1,"animload_ped_f") )
{
Dbg_MsgAssert( pSkeleton, ( "Skeleton has not been assigned to this model yet" ) );
Script::CStruct* pBodyShapeStructure = Script::GetStructure( bodyShapeName, Script::ASSERT );
Mth::Vector theScale( 1.0f, 1.0f, 1.0f );
if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) )
{
Mth::Vector vec = pModel->GetScale();
vec[X] *= theScale[X];
vec[Y] *= theScale[Y];
vec[Z] *= theScale[Z];
// if the body shape has a scale
pModel->SetScale( vec );
}
pSkeleton->ApplyBoneScale( pBodyShapeStructure );
}
return true;
}
return false;
}
#endif