void EntityLODComponent::UpdateLODLevel(int newLevel)
{
  if (m_iCurrentLevel==newLevel)
  {
    return;
  }

  // change the mesh and the animation config used
  int oldLevel = m_iCurrentLevel;
  VLODLevelInfo &newLODInfo = GetLevelInfo(newLevel);
  m_iCurrentLevel=newLevel;
  m_pOwner->SetMesh(newLODInfo.m_spMesh);
  m_pOwner->SetAnimConfig(newLODInfo.m_spAnimConfig);

  // synchronize animation tracks of both LODs
  if (oldLevel>=0 && oldLevel<m_iLevelCount)
  {
	  VLODLevelInfo &oldLODInfo = GetLevelInfo(oldLevel);
	  IVisAnimResultGenerator_cl* oldLODAnimInput = oldLODInfo.m_spAnimConfig->GetFinalResult()->GetSkeletalAnimInput();

	  float currAnimTime = 0.0f;
	  if ( oldLODAnimInput->IsOfType( V_RUNTIME_CLASS(VisSkeletalAnimControl_cl) ) )
	  {
        VisSkeletalAnimControl_cl* oldLODAanimController = static_cast< VisSkeletalAnimControl_cl* >( oldLODAnimInput );
	     currAnimTime = oldLODAanimController->GetCurrentSequenceTime();
	  }

	  IVisAnimResultGenerator_cl* newLODAnimInput = newLODInfo.m_spAnimConfig->GetFinalResult()->GetSkeletalAnimInput();
	  if ( newLODAnimInput->IsOfType( V_RUNTIME_CLASS(VisSkeletalAnimControl_cl) ) )
	  {
	     VisSkeletalAnimControl_cl* newLODAanimController = static_cast< VisSkeletalAnimControl_cl* >( newLODAnimInput );
	     newLODAanimController->SetCurrentSequenceTime( currAnimTime );
	  }
  }
}
示例#2
0
BOOL VBlobShadow::CanAttachToObject(VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;

  if ((!pObject->IsOfType(V_RUNTIME_CLASS(VisObject3D_cl))) && (!pObject->IsOfType(V_RUNTIME_CLASS(VisStaticMeshInstance_cl))))
  {
    sErrorMsgOut = "Component can only be added to instances of VisObject3D_cl and VisStaticMeshInstance_cl or derived classes.";
    return FALSE;
  }
  
  return TRUE;
}
示例#3
0
void RPG_Pickup::CheckCharacterContact()
{
  VArray<RPG_Character*> const& characters = RPG_GameManager::s_instance.GetCharacters();

  hkvVec3 const& currentPosition = GetPosition();

  for(int index = 0; index < characters.GetSize(); ++index)
  {
    RPG_Character* character = characters.GetAt(index);

    // only target players
    if(!character->IsOfType(V_RUNTIME_CLASS(RPG_PlayerCharacter)))
    {
      continue;
    }

    hkvVec3 const& targetPosition = character->GetPosition();

    float currentRangeSquared = (currentPosition - targetPosition).getLengthSquared();

    if(currentRangeSquared <= m_pickupRadius * m_pickupRadius)
    {
      // call OnPickup for subclasses
      OnPickup(character);

      // play the pickup effect
      CreateEffect(PKFX_Pickup, GetPosition(), GetOrientation());

      // setup this object for deletion
      DisposeObject();
    }
  }
}
BOOL VTimeOfDayComponent::CanAttachToObject(VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (pObject->IsOfType(V_RUNTIME_CLASS(VisObject3D_cl)))
    return TRUE;

  return FALSE;
}
void VRendererNodeCommon::UpdateTimeOfDay()
{
  // Update the active sky.
  IVSky* pSky = m_spSky;
  if (pSky == NULL)
  {
    pSky = Vision::World.GetActiveSky();
  }
  if (pSky != NULL)
  {
    pSky->Tick(0);
  }

  // Update Time Of Day handler.
  IVTimeOfDay* pTimeOfDayInterface = Vision::Renderer.GetTimeOfDayHandler();
  if (pTimeOfDayInterface != NULL)
  {
    VASSERT_MSG(pTimeOfDayInterface->IsOfType(V_RUNTIME_CLASS(VTimeOfDay)),
      "Incompatible time of day handler installed - has to be VTimeOfDay or a subclass of it!");
    VTimeOfDay* pTimeOfDay = vstatic_cast<VTimeOfDay*>(pTimeOfDayInterface);
    pTimeOfDay->UpdateFogParameters();

    VColorRef vAmbientColor = pTimeOfDay->GetAmbientColor();
    Vision::Renderer.SetGlobalAmbientColor(vAmbientColor.ToFloat().getAsVec4(1.0f));
  }
  else
  {
    // Set the default global ambient color.
    Vision::Renderer.SetGlobalAmbientColor(Vision::Renderer.GetDefaultGlobalAmbientColor());
  }
}
示例#6
0
/// Checks if this is a valid entity for triggering.
bool RPG_Trigger::IsValidEntity(VisBaseEntity_cl* entity) const
{
  if(!entity)
  {
    return false;
  }

  // make sure this entity isn't Entering again before leaving.
  if(m_insideEntities.Find(entity) > -1)
  {
    //VASSERT_MSG(false, "RPG_Trigger::IsValidEntity - Entity is trying to Enter more than once before Leaving!");
    return false;
  }


  bool canTrigger = false;

  if(m_acceptOnlyPlayers)
  {
    // make sure this is a player
    if(entity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_PlayerControllerComponent)))
    {
      canTrigger = true;
    }
  }
  else
  {
    // go ahead if we have no restrictions
    canTrigger = true;
  }

  return canTrigger;
}
示例#7
0
void RPG_GameManager::OnAfterSceneLoaded()
{
  RPG_RendererUtil::StoreViewParams(m_storedViewParams);

  // Set up game view params
  RPG_ViewParams viewParams = m_storedViewParams;
  {
    viewParams.m_projectionType = VIS_PROJECTIONTYPE_PERSPECTIVE;
    viewParams.m_nearClip = 50.f;
    viewParams.m_farClip = 2500.f;
    viewParams.m_fovX = 0.f;
    viewParams.m_fovY = 50.f;
  }

  RPG_RendererUtil::LoadViewParams(viewParams);

  // Local player
  VisBaseEntity_cl* playerEntity = SpawnPlayer("Prefabs\\Demo_Player_Hero.vprefab");

  if(playerEntity)
  {
    RPG_PlayerControllerComponent *const playerController = static_cast<RPG_PlayerControllerComponent*>
      (playerEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_PlayerControllerComponent)));

    VASSERT(playerController);
    if(playerController)
    {
      RPG_PlayerUI::s_instance.SetController(playerController);
    }
  }
}
void VTimeOfDayComponent::SetOwner(VisTypedEngineObject_cl *pOwner)
{
  IVObjectComponent::SetOwner(pOwner);
  m_bIsLightClass = pOwner!=NULL && pOwner->IsOfType(V_RUNTIME_CLASS(VisLightSource_cl));
  if (m_bIsLightClass)
    m_iColor = ((VisLightSource_cl*)pOwner)->GetColor();
}
示例#9
0
BOOL VPrefab::Instantiate(VPrefabInstanceInfo &info)
{
  EnsureLoaded();
  if (!IsLoaded())
    return FALSE;

  // serialize from memory block (class VPrefabShapesArchive provides with transformation)
  VMemBlockWrapperStream inStream(m_BinaryBlock.GetBuffer(), m_iSize);
  VPrefabShapesArchive ar(&inStream, info);
  ar.SetLoadingVersion(m_Header.m_iArchiveVersion);

  if (info.m_bOutputInstances)
    info.m_Instances.EnsureSize(m_Header.m_iRootObjectCount);

  info.m_iInstanceCount = m_Header.m_iRootObjectCount;
  for (int i=0;i<m_Header.m_iRootObjectCount;i++)
  {
    VTypedObject *pObj = ar.ReadObject(NULL);
    if (info.m_bOutputInstances)
      info.m_Instances[i] = pObj;
    if (info.m_pParentObject!=NULL && pObj!=NULL && pObj->IsOfType(V_RUNTIME_CLASS(VisObject3D_cl)))
    {
      VisObject3D_cl *pObj3D = (VisObject3D_cl *)pObj;
      if (pObj3D->GetParent()==NULL) // this is how we identify the root elements
        pObj3D->AttachToParent(info.m_pParentObject);
    }
  }

  ar.Close();

  return TRUE;
}
示例#10
0
VisBaseEntity_cl* MyGameManager::SpawnPlayer(const VString& prefabName, hkvVec3 const& position, hkvVec3 const& orientation)
{
	
    VisBaseEntity_cl* playerEntity = CreateEntityFromPrefab(prefabName, position, orientation);
    playerEntity->InitFunction();
    
    if(playerEntity)
    {
        PlayerComponent *const playerController = static_cast<PlayerComponent*>
        (playerEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(PlayerComponent)));
        
        //VASSERT(playerController);
        if(playerController)
        {
            spGUIContext = new VGUIMainContext(NULL);
            // Create a GUI context
            mDialog = new PlayerDialog(playerController);
            mDialog->InitDialog(spGUIContext, NULL, NULL);
            spGUIContext->ShowDialog(mDialog);
            spGUIContext->SetActivate(true);
        }
        
        m_playerEntity = playerEntity->GetWeakReference();
        //Vision::Message.Add(1, "Spawn");
    }
    
    return playerEntity;
}
// ----------------------------------------------------------------------------
void vHavokChainAnimation::CommonInit()
{
  CommonDeinit();

  if (!m_pOwner || !m_pOwner->IsOfType(V_RUNTIME_CLASS(VisBaseEntity_cl)))
    return;

  Vision::Callbacks.OnUpdateSceneFinished += this;

  m_pOwnerEntity = static_cast<VisBaseEntity_cl*>(m_pOwner);
  VisAnimConfig_cl *pAnimConfig = m_pOwnerEntity->GetAnimConfig();

  // If there is no AnimConfig, but we can get a skeleton, create the AnimConfig.
  if (!pAnimConfig)
  {
    VDynamicMesh *pMesh = m_pOwnerEntity->GetMesh();
    VisSkeleton_cl *pSkeleton = pMesh ? pMesh->GetSkeleton() : NULL;
    if (pSkeleton)
    {
      VisAnimFinalSkeletalResult_cl* pFinalSkeletalResult;
      pAnimConfig = VisAnimConfig_cl::CreateSkeletalConfig(pMesh, &pFinalSkeletalResult);
      m_pOwnerEntity->SetAnimConfig(pAnimConfig);
    }
  }
}
示例#12
0
///< Spawn the Ai Character and perform any setup we need to of them.
void RPG_AiSpawnPoint::OnSpawn()
{
  // only spawn if we haven't already. @todo: add support for spawning waves of characters.
  if(m_spawned)
  {
    return;
  }

  m_spawned = true;

  bool success = false;

  if(!m_prefabName.IsEmpty())
  {
    VisBaseEntity_cl* entity = RPG_GameManager::s_instance.CreateEntityFromPrefab(m_prefabName, GetPosition(), GetOrientation());
    
    if(entity)
    {
      if(entity->IsOfType(V_RUNTIME_CLASS(RPG_Character)))
      {
        m_character = static_cast<RPG_Character*>(entity);
      }

      success = true;
    }
  }
  else if(!m_spawnScript.IsEmpty())
  {
    VisBaseEntity_cl* entity = RPG_GameManager::s_instance.CreateEntityFromScript(m_spawnScript, GetPosition(), GetOrientation());
    VASSERT(entity);

    if(entity)
    {
      if(entity->IsOfType(V_RUNTIME_CLASS(RPG_Character)))
      {
        m_character = static_cast<RPG_Character*>(entity);
      }

      success = true;
    }
  }

  if(!success)  
  {
    hkvLog::Warning("Ai Spawner has no Prefab defined, and no valid Script definition!");
  }
}
示例#13
0
void PlayerUIDialog::ContinueMeleeAttack(RPG_Character* characterEntity, RPG_DamageableEntity * const targetEntity)
{
  VASSERT(characterEntity);
  const bool hitHighlightable = targetEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_HighlightableComponent));
  const bool hitAttackable = targetEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_AttackableComponent));
  //@debug: test log output
  //Vision::Error.Warning(hitEntity->GetMesh()->GetFilename());

  if (hitHighlightable || hitAttackable)
  {
    if (hitHighlightable)  //"(RPG) Highlightable"
    {
      //Vision::Error.Warning("(RPG) Highlightable");

#ifdef _DEBUG
      if (m_debugUI)
      {
        Vision::Message.DrawMessage3D("(RPG) Highlightable", targetEntity->GetPosition());
      }
#endif
    }

    if (hitAttackable)  //"(RPG) Attackable"
    {
      // if player's holding down primary action input on an existing attack action, this sets its continue flag to true.
      if (characterEntity->GetActionHandler().IsPerformingAction(AT_MeleeAttack))
      {
        characterEntity->GetActionHandler().SetActiveActionFlags(1);
      }

#ifdef _DEBUG
      if (m_debugUI)
      {
        Vision::Message.DrawMessage3D("(RPG) Attackable", targetEntity->GetPosition() + hkvVec3(0.0f, 0.0f, 16.0f)); // (@hack: offset a little to avoid overwriting other component messages)
      }
#endif
    }
  }
  else
  {
    if(!characterEntity->IsAttacking())
    {
      // fall back to the move if the entity we hit wasn't a highlightable or attackable.
      RequestMove(characterEntity, false);
    }
  }
}
bool VPathRenderingData::SetPathObject(VTypedObject* pObject)
{
  if (!pObject->IsOfType(V_RUNTIME_CLASS(VisPath_cl)))
    return false;

  m_spPathObject = (VisPath_cl*)pObject;
  return true;
}
bool vHavokConstraintChainRenderingData::SetPathObject(VTypedObject* pObject)
{
    if (!pObject->IsOfType(V_RUNTIME_CLASS(vHavokConstraintChain)))
        return false;

    m_spPathObject = (vHavokConstraintChain*)pObject;
    return true;
}
void VTerrainVisibilityInfo::Set(IVisVisibilityCollector_cl *pCollector,const VTerrainConfig &config)
{
  hkvVec3 vCamPos(hkvNoInitialization), vCamDir(hkvNoInitialization);
  
  pCollector->GetSourceObject()->GetPosition(vCamPos);
  vCamDir = pCollector->GetSourceObject()->GetDirection();

  VisRenderContext_cl *pLODContext = pCollector->GetLODReferenceRenderContext();
  VASSERT(pLODContext!=NULL && pLODContext->GetCamera()!=NULL);
  pLODContext->GetCamera()->GetPosition(m_vCamLODPos);

  m_vCamPos.FromRenderSpace(config,(const hkvVec3& )vCamPos);
  m_vCamVisPos = vCamPos;
  m_CameraPlane.setFromPointAndNormal(vCamPos,vCamDir);
  VASSERT(m_vCamPos.IsValid(config));

  // compute the rest
  float fNear,fFar;
  pCollector->GetClipPlanes(fNear,fFar);
  m_fMaxViewDistance = fFar;

  m_fLODDistanceInvScale = pLODContext->GetLODDistanceScaling() * VTerrainSectorManager::g_fDecorationDistanceInvScaling;
  if (m_fLODDistanceInvScale>0.f)
    m_fLODDistanceScale = 1.f/m_fLODDistanceInvScale;
  else 
    m_fLODDistanceScale = 0.f;

  // overestimate
  config.GetViewDistanceBox(m_CamBBox, m_vCamVisPos, fFar);
  m_VisibleRangeBox.FromRenderSpace(config,m_CamBBox);
  m_iContextFilterMask = pCollector->GetFilterBitmask();

  // reset min/max
  m_iVisibleSectorRange[0] = m_iVisibleSectorRange[1] = 32000;
  m_iVisibleSectorRange[2] = m_iVisibleSectorRange[3] = -32000;

  // reset some of the values:
  m_iVisibleDecorationCount = 0;
  m_iEstimatedDecorationCount = 0;

  static bool bEnableOpt = true;

  // shadowmap related:
  if (bEnableOpt && pCollector->GetTypeId()==V_RUNTIME_CLASS(VShadowmapVisibilityCollector))
  {
    m_pSMGenerator = ((VShadowmapVisibilityCollector *)pCollector)->m_pSMGenerator;
    float fShadowExtrusionFactor = m_pSMGenerator->GetShadowMapComponent()->GetShadowBoxExtrudeMultiplier();
    m_vShadowExtrusion = m_pSMGenerator->GetDirection();
    if (m_vShadowExtrusion.z<-0.01f) // normalize height
      m_vShadowExtrusion.z *= (-1.f/m_vShadowExtrusion.z);
    m_vShadowExtrusion *= fShadowExtrusionFactor;
    m_bCastDynamicShadows = (m_pSMGenerator->GetShadowMapComponent()->GetGeometryTypes()&SHADOW_CASTER_TERRAIN)>0;
  }
  else
    m_pSMGenerator = NULL;

}
BOOL VPathRenderingMetaData::CanAttachToObject(VisTypedEngineObject_cl* pObject, VString& sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;

  if (!pObject->IsOfType(V_RUNTIME_CLASS(VisPath_cl)))
    return FALSE;

  return TRUE;
}
示例#18
0
void RPG_Pickup::UpdateMagnetForces(float const deltaTime)
{
  if(m_usePhysics)
  {
    // find the player
    VArray<RPG_Character*> const& characters = RPG_GameManager::s_instance.GetCharacters();

    hkvVec3 const& currentPosition = GetPosition();

    RPG_PlayerCharacter* player = NULL;

    // find a player in range
    for(int index = 0; index < characters.GetSize(); ++index)
    {
      RPG_Character* character = characters.GetAt(index);

      // only target apparently alive players
      if(!character->IsOfType(V_RUNTIME_CLASS(RPG_PlayerCharacter)) ||
        character->IsDead() ||
        character->IsFeigningDeath())
      {
        continue;
      }

      player = static_cast<RPG_PlayerCharacter*>(character);

      break;
    }

    if(player)
    {
      hkvVec3 targetPosition = player->GetPosition();
      targetPosition.z += 0.5f * player->GetBoundingBox().getSizeZ();

      float distance = (currentPosition - targetPosition).getLength();

      if(distance < m_pickupRadius)
      {
        distance = m_pickupRadius;
      }

      float const distanceSquared = distance * distance;

      if(distanceSquared < m_magnetMaxDistance * m_magnetMaxDistance)
      {
        hkvVec3 direction = targetPosition - GetPosition();
        direction.normalizeIfNotZero();

        float magnitude = m_magnetSpeedMultiplier * (1.0f / distanceSquared - m_magnetMaxDistanceInverseSquared);
        m_currentMagnetSpeed += deltaTime * magnitude * RPG_HEALTH_PICKUP_MAGNET_SPEED_MULTIPLIER_CONVERSION_VALUE;
        m_currentMagnetVelocity = m_currentMagnetSpeed * direction;
      }
    }
  }
}
示例#19
0
void RPG_DestructibleEntity::AddComponents()
{
  // Add an attackable component if none was added in the editor
  if (!Components().GetComponentOfType(V_RUNTIME_CLASS(RPG_AttackableComponent)))
  {
    RPG_AttackableComponent* attackableComponent = new RPG_AttackableComponent;
    AddComponent(attackableComponent);
  }

  // Add a rigid body component if none already exists
  if (!Components().GetComponentOfType(V_RUNTIME_CLASS(vHavokRigidBody)))
  {
    // Rigid Body
    vHavokRigidBody* rigidBody = new vHavokRigidBody;
    rigidBody->Shape_Type = ShapeType_CYLINDER;
    rigidBody->Shape_Radius = m_collisionRadius;
    rigidBody->Shape_Height = m_collisionHeight;
    rigidBody->Havok_MotionType = MotionType_FIXED;
    AddComponent(rigidBody);
  }
}
示例#20
0
/// Finds and stores the level info object.
void RPG_GameManager::FindLevelInfo()
{
  // find the level info object
  VisBaseEntity_cl* levelInfo = Vision::Game.SearchEntity(RPG_LevelInfo::GetStaticKey());

  if(levelInfo &&
    levelInfo->IsOfType(V_RUNTIME_CLASS(RPG_LevelInfo)))
  {
    // store the level info object if we found one
    m_levelInfo = static_cast<RPG_LevelInfo*>(levelInfo);
  }
}
示例#21
0
void PlayerUIDialog::TryMeleeAttack(RPG_Character* characterEntity, RPG_DamageableEntity * const targetEntity)
{ 
  VASSERT(characterEntity);

  const bool hitHighlightable = targetEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_HighlightableComponent));
  const bool hitAttackable = targetEntity->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_AttackableComponent));
  //@debug: test log output
  //Vision::Error.Warning(hitEntity->GetMesh()->GetFilename());

  if (hitHighlightable || hitAttackable)
  {
    if (hitHighlightable)  //"(RPG) Highlightable"
    {
      //Vision::Error.Warning("(RPG) Highlightable");
    }

    if (hitAttackable)  //"(RPG) Attackable"
    {
      //Vision::Error.Warning("(RPG) Attackable");
      if(characterEntity->IsTargetWithinAttackRange(targetEntity))
      {
        characterEntity->GetActionHandler().PerformAction(AT_MeleeAttack, true, targetEntity, hkvVec3(0, 0, 0), 0);
      }
      else
      {
        characterEntity->GetActionHandler().PerformAction(AT_Move, true, targetEntity, hkvVec3(0, 0, 0), MF_AttackRange);
        characterEntity->GetActionHandler().PerformAction(AT_MeleeAttack, false, targetEntity);
      }
    }
  }
  else
  {
    // fall back to the move if the entity we hit wasn't a highlightable or attackable.
    if (!characterEntity->IsAttacking())
    {
      RequestMove(characterEntity, true);
    }
  }
}
BOOL VAnimationComponent::CanAttachToObject(VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return false;

  if (!pObject->IsOfType(V_RUNTIME_CLASS(VisBaseEntity_cl)))
  {
    sErrorMsgOut = "Component can only be added to instances of VisBaseEntity_cl or derived classes.";
    return FALSE;
  }

  return IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut);
}
示例#23
0
VDialog * VDlgControlBase::GetParentDialog() const
{
  // find the first owner of type dialog
  VWindowBase *pParent = GetParent();
  VType *pDlgType = V_RUNTIME_CLASS(VDialog);
  while (pParent)
  {
    if (pParent->IsOfType(pDlgType))
      return (VDialog *)pParent;
    pParent = pParent->GetParent();
  }
  return NULL;
}
BOOL VMeshPaintingComponent::CanAttachToObject(VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
#if defined( HK_ANARCHY )
  sErrorMsgOut = "Component type not supported in Anarchy.";
  return FALSE;
#else
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;
  if (pObject->IsOfType(V_RUNTIME_CLASS(VisStaticMeshInstance_cl)))
    return TRUE;
  return FALSE;
#endif
}
BOOL RPG_MeshTrailEffectComponent::CanAttachToObject(VisTypedEngineObject_cl *object, VString& errorOut)
{
  if(!IVObjectComponent::CanAttachToObject(object, errorOut))
  {
    return FALSE;
  }

  bool const isBaseEntity = (object->IsOfType(V_RUNTIME_CLASS(VisBaseEntity_cl)) == TRUE);
  if(!isBaseEntity)
  {
    errorOut = "VisBaseEntity_cl (or derived) required";
    return FALSE;
  }

  bool const hasWeaponTrailEffect = (object->Components().GetComponentOfBaseType(V_RUNTIME_CLASS(RPG_MeshTrailEffectComponent)) != NULL);
  if(hasWeaponTrailEffect)
  {
    errorOut = "RPG_MeshTrailEffectComponent (or derived) already attached";
    return FALSE;
  }

  return TRUE;
}
// ----------------------------------------------------------------------------
BOOL vHavokChainAnimation::CanAttachToObject(
  VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;

  if (!pObject->IsOfType(V_RUNTIME_CLASS(VisBaseEntity_cl)))
  {
    sErrorMsgOut = "Component can only be added to entities.";
    return FALSE;
  }

  return TRUE;
}
// ----------------------------------------------------------------------------
BOOL vHavokConstraintChainRendererBase::CanAttachToObject(
  VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;

  if (!pObject->IsOfType(V_RUNTIME_CLASS(vHavokConstraintChain)))
  {
    sErrorMsgOut = "Component can only be added to Havok constraint chains.";
    return FALSE;
  }

  return TRUE;
}
void VPathRenderingMetaData::OnVariableValueChanged(VisVariable_cl* pVar, const char* value)
{
  IVObjectComponent::OnVariableValueChanged(pVar, value);

  // path renderers need to be re-initialized when variables are changed
  if (GetOwner() != NULL)
  {
    for (int i = 0; i < GetOwner()->Components().Count(); i++)
    {
      IVObjectComponent* pComp = GetOwner()->Components().GetAt(i);
      if (pComp != NULL && pComp->IsOfType(V_RUNTIME_CLASS(VPathRendererBase)))
        ((VPathRendererBase*)pComp)->OnDataChanged();
    }
  }
}
BOOL VThrowItemComponent::CanAttachToObject(VisTypedEngineObject_cl *pObject, VString &sErrorMsgOut)
{
  if (!IVObjectComponent::CanAttachToObject(pObject, sErrorMsgOut))
    return FALSE;

  // Define criteria here that allows the editor to attach this component to the passed object.
  // In our example, the object should be derived from VisObject3D_cl to be positionable.
  BOOL bIsValidClass = pObject->IsOfType(V_RUNTIME_CLASS(VisBaseEntity_cl));
  if (!bIsValidClass)
  {
    sErrorMsgOut += "Component can only be added to instances of VisBaseEntity_cl or derived classes.";
    return FALSE;
  }

  return TRUE;
}
// ----------------------------------------------------------------------------
bool vHavokConstraintChainRendererBase::DoInit()
{
  // Get the constraint chain that owns this component
  VisTypedEngineObject_cl *pOwner = GetOwner();
  m_pConstraintChain = (pOwner && pOwner->IsOfType(V_RUNTIME_CLASS(vHavokConstraintChain)))
    ? static_cast<vHavokConstraintChain*>(pOwner)
    : NULL;
  if (!m_pConstraintChain)
    return false;

  // Register as a rendering hook
  Vision::Callbacks.OnRenderHook += this;
  Vision::Callbacks.OnUpdateSceneBegin += this;

  return true;
}