//**************************************************************************** //* MODULE: Gel/Components //* FILENAME: ModelComponent.cpp //* OWNER: Gary Jesdanun //* CREATION DATE: 10/17/2002 //**************************************************************************** #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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(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; iClear(); ++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; imDispRotating) { 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; imDispRotating) { 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; iGetID()) ) ); 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