Example #1
0
//------------------------------------------------------------------------
bool CVehiclePartAnimated::ChangeState(EVehiclePartState state, int flags)
{
	if ((state == eVGS_Default) && m_initialiseOnChangeState)
	{
		// Initialise!
		// Having to do this because of the way the glass code
		// swaps a cstatobj. The way the vehicle code stores its
		// statobj in m_intactStatObjs is going to need reviewing
		if (m_pCharInstance)
		{
			ISkeletonPose*    pSkeletonPose                     = m_pCharInstance->GetISkeletonPose();
			IDefaultSkeleton &rIDefaultSkeleton                 = m_pCharInstance->GetIDefaultSkeleton();
			ISkeletonPose*    pSkeletonPoseDestroyed            = m_pCharInstanceDestroyed ? m_pCharInstanceDestroyed->GetISkeletonPose() : NULL;
			IDefaultSkeleton* pICharacterModelSkeletonDestroyed = m_pCharInstanceDestroyed ? &m_pCharInstanceDestroyed->GetIDefaultSkeleton() : NULL;
			if (pSkeletonPose)
			{
				const bool bDestroyedSkelExists = pSkeletonPoseDestroyed && pICharacterModelSkeletonDestroyed;
				for (uint32 i = 0; i < rIDefaultSkeleton.GetJointCount(); i++)
				{
					if (IStatObj* pStatObjIntact = pSkeletonPose->GetStatObjOnJoint(i))
					{
						const char* jointName = rIDefaultSkeleton.GetJointNameByID(i);

						if (m_intactStatObjs.find(CONST_TEMP_STRING(jointName)) == m_intactStatObjs.end())
						{
							m_intactStatObjs.insert(TStringStatObjMap::value_type(jointName, pStatObjIntact));
						}

						// tell the streaming engine to stream destroyed version together with non destroyed
						if (bDestroyedSkelExists && i < pICharacterModelSkeletonDestroyed->GetJointCount())
						{
							if (IStatObj* pStatObjIntactDestroyed = pSkeletonPoseDestroyed->GetStatObjOnJoint(i))
							{
								pStatObjIntact->SetStreamingDependencyFilePath(pStatObjIntactDestroyed->GetFilePath());
							}
						}
					}
				}
			}
		}
		m_initialiseOnChangeState = false;
	}

	bool change = CVehiclePartBase::ChangeState(state, flags);

	if (state == eVGS_Default && !change)
	{
		// need to restore state if one of the children is in higher state
		EVehiclePartState maxState = GetMaxState();

		if (maxState > m_state)
			change = true;
	}

	if (!change)
	{
		return false;
	}

	if (state == eVGS_Destroyed)
	{
		if (m_ignoreDestroyedState)
			return false;

		if (m_pCharInstance && m_pCharInstanceDestroyed)
		{
			ISkeletonPose*    pSkeletonPose     = m_pCharInstance->GetISkeletonPose();
			IDefaultSkeleton &rIDefaultSkeleton = m_pCharInstance->GetIDefaultSkeleton();
			if (pSkeletonPose)
			{
				IMaterial* pDestroyedMaterial = m_pVehicle->GetDestroyedMaterial();

				for (uint32 i = 0; i < rIDefaultSkeleton.GetJointCount(); i++)
				{
					if (IStatObj* pStatObjIntact = pSkeletonPose->GetStatObjOnJoint(i))
					{
						const char* jointName = rIDefaultSkeleton.GetJointNameByID(i);
						IStatObj*   pStatObj  = GetDestroyedGeometry(jointName);

						// sets new StatObj to joint, if null, removes it.
						// object whose name includes "proxy" are not removed.
						if (pStatObj || !strstr(jointName, "proxy"))
						{
							SetCGASlot(i, pStatObj);

							if (pStatObj && !pDestroyedMaterial)
							{
								if (IMaterial* pMaterial = pStatObj->GetMaterial())
									SetMaterial(pMaterial);
							}

#if ENABLE_VEHICLE_DEBUG
							if (IsDebugParts())
							{
								CryLog("swapping StatObj on joint %u (%s) -> %s", i, jointName, pStatObj ? pStatObj->GetGeoName() : "<NULL>");
							}
#endif
						}
					}
				}

				FlagSkeleton(pSkeletonPose, rIDefaultSkeleton);

				for (TStringVehiclePartMap::iterator ite = m_jointParts.begin(); ite != m_jointParts.end(); ++ite)
				{
					IVehiclePart* pPart = ite->second;
					pPart->ChangeState(state, flags | eVPSF_Physicalize);
				}

				CryCharAnimationParams animParams;
				animParams.m_nFlags |= CA_LOOP_ANIMATION;
				// pSkeleton->SetRedirectToLayer0(1);
				// pSkeleton->StartAnimation("Default",0,  0,0, animParams);  // [MR: commented out on Ivos request]

				if (pDestroyedMaterial)
				{
					SetMaterial(pDestroyedMaterial);
				}
			}
		}
	}
	else if (state == eVGS_Default)
	{
		if (m_pCharInstance && m_pCharInstanceDestroyed)
		{
			// reset material (in case we replaced it with the destroyed material)
			IMaterial* pMaterial = m_pVehicle->GetPaintMaterial();
			if (!pMaterial)
			{
				// no paint, so revert to the material already set on the character
				pMaterial = m_pCharInstance->GetIMaterial();
			}
			if (pMaterial)
			{
				SetMaterial(pMaterial);
			}

			IDefaultSkeleton &rIDefaultSkeleton = m_pCharInstance->GetIDefaultSkeleton();
			{
				for (TStringStatObjMap::iterator ite = m_intactStatObjs.begin(); ite != m_intactStatObjs.end(); ++ite)
				{
					const string &jointName = ite->first;
					IStatObj*     pStatObj  = ite->second;

					int16 jointId = rIDefaultSkeleton.GetJointIDByName(jointName.c_str());
					if (jointId > -1)
					{
						// if compound StatObj (from deformation), use first SubObj for restoring
						if (pStatObj != NULL)
						{
							if (!pStatObj->GetRenderMesh() && pStatObj->GetSubObjectCount() > 0)
							{
								pStatObj = pStatObj->GetSubObject(0)->pStatObj;
							}

							SetCGASlot(jointId, pStatObj);

#if ENABLE_VEHICLE_DEBUG
							if (IsDebugParts())
								CryLog("restoring StatObj on joint %i (%s) -> %s", jointId, jointName.c_str(), pStatObj ? pStatObj->GetGeoName() : "<NULL>");
#endif
						}

						TStringVehiclePartMap::iterator it = m_jointParts.find(jointName);
						if (it != m_jointParts.end())
						{
							it->second->ChangeState(state, flags & ~eVPSF_Physicalize | eVPSF_Force);
						}
					}
				}
				flags |= eVPSF_Physicalize;
			}
		}
	}

	m_state = state;

	// physicalize after all parts have been restored
	if (flags & eVPSF_Physicalize && GetEntity()->GetPhysics())
	{
		Physicalize();
		for (TStringVehiclePartMap::iterator it = m_jointParts.begin(); it != m_jointParts.end(); ++it)
		{
			it->second->Physicalize();
		}
	}

	return true;
}
//------------------------------------------------------------------------
void CVehicleDamageBehaviorDetachPart::OnDamageEvent(EVehicleDamageBehaviorEvent event, const SVehicleDamageBehaviorEventParams& behaviorParams)
{
	if (event == eVDBE_Repair)
		return;

	if (!m_detachedEntityId && behaviorParams.componentDamageRatio >= 1.0f)
	{
    CVehiclePartBase* pPart = (CVehiclePartBase*)m_pVehicle->GetPart(m_partName.c_str());
    if (!pPart || !pPart->GetStatObj())
      return;

		if (max(1.f-behaviorParams.randomness, pPart->GetDetachProbability()) < cry_random(0.0f, 1.0f)) 
			return;

		IEntity* pDetachedEntity = SpawnDetachedEntity();
		if (!pDetachedEntity)
			return;

		m_detachedEntityId = pDetachedEntity->GetId();

    const Matrix34& partWorldTM = pPart->GetWorldTM();
    pDetachedEntity->SetWorldTM(partWorldTM);
		
    MovePartToTheNewEntity(pDetachedEntity, pPart);
		
		SEntityPhysicalizeParams physicsParams;
		physicsParams.mass = pPart->GetMass();
		physicsParams.type = PE_RIGID;    		    
		physicsParams.nSlot = 0;
		pDetachedEntity->Physicalize(physicsParams);
  
    IPhysicalEntity* pPhysics = pDetachedEntity->GetPhysics();
    if (pPhysics)
    {
      pe_params_part params;      
      params.flagsOR = geom_collides|geom_floats;
      params.flagsColliderAND = ~geom_colltype3;
      params.flagsColliderOR = geom_colltype0;
      pPhysics->SetParams(&params);

      pe_action_add_constraint ac;
      ac.flags = constraint_inactive|constraint_ignore_buddy;
      ac.pBuddy = m_pVehicle->GetEntity()->GetPhysics();
      ac.pt[0].Set(0,0,0);
      pPhysics->Action(&ac);
			
			// after 1s, remove the constraint again
			m_pVehicle->SetTimer(-1, 1000, this);
    
		  // set the impulse
		  const Vec3& velocity = m_pVehicle->GetStatus().vel;  				  		  
		  Vec3 baseForce = m_pVehicle->GetEntity()->GetWorldTM().TransformVector(pPart->GetDetachBaseForce());
		  baseForce *= cry_random(6.0f, 10.0f);
  		
      pe_action_impulse actionImpulse;
		  actionImpulse.impulse = physicsParams.mass * (velocity + baseForce);
      actionImpulse.angImpulse = physicsParams.mass * Vec3(cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f));
      actionImpulse.iApplyTime = 1;
		  
      pPhysics->Action(&actionImpulse);		
    }

		// copy vehicle's material to new entity (fixes detaching parts from vehicles with different paints),
		// or specify the destroyed material if it exists

		IStatObj* pExternalStatObj = pPart->GetExternalGeometry(false);	// Get undamaged external geometry (if any)
		IMaterial *pMaterial = pExternalStatObj ? pExternalStatObj->GetMaterial() : m_pVehicle->GetEntity()->GetMaterial();

		if(event == eVDBE_VehicleDestroyed || event == eVDBE_Hit)
		{
			if (pExternalStatObj)
			{
				if (IStatObj* pStatObj = pPart->GetExternalGeometry(true))     // Try to get the destroyed, external geometry material
					pMaterial = pStatObj->GetMaterial();
			}
			else if (m_pVehicle->GetDestroyedMaterial())  // If there is no external geometry, try the vehicle's destroyed material
			{
				pMaterial = m_pVehicle->GetDestroyedMaterial();
			}
		}

		pDetachedEntity->SetMaterial(pMaterial);
		
		AttachParticleEffect(pDetachedEntity, m_pEffect);

		if (m_notifyMovement)
		{
			SVehicleMovementEventParams params;
			params.iValue = pPart->GetIndex();
			m_pVehicle->GetMovement()->OnEvent(IVehicleMovement::eVME_PartDetached, params);
		}
	}
}
//--------------------------------------------------------------------------------------------------
// Name: ExtractPhysDataFromEvent
// Desc: Extracts collider's physical data from an event
// Note 1: Ideally *ALL* of this should be calculated offline and the minimal data loaded
// Note 2: We're currently duplicating some work done in CryAction, so should be reading that in
//--------------------------------------------------------------------------------------------------
bool CBreakableGlassSystem::ExtractPhysDataFromEvent(const EventPhysCollision& physEvent, SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
{
	if (IPhysicalEntity* pPhysEntity = physEvent.pEntity[PHYSEVENT_COLLIDEE])
	{
		// Get collider entity data
		const int entType = pPhysEntity->GetiForeignData();
		const int entPart = physEvent.partid[PHYSEVENT_COLLIDEE];

		// Local output data
		IStatObj* pStatObj = NULL;
		IMaterial* pRenderMat = NULL;
		phys_geometry* pPhysGeom = NULL;
		uint renderFlags = 0;

		Matrix34A entityMat;
		entityMat.SetIdentity();

		// Only handling simple objects at the moment
		const pe_type physType = pPhysEntity->GetType();

		if (physType == PE_STATIC || physType == PE_RIGID)
		{
			// Entity or static object?
			if (entType == PHYS_FOREIGN_ID_ENTITY)
			{
				IEntity* pEntity = (IEntity*)pPhysEntity->GetForeignData(PHYS_FOREIGN_ID_ENTITY);

				pStatObj = pEntity->GetStatObj(entPart);
				entityMat = pEntity->GetSlotWorldTM(entPart);

				if (IEntityRenderProxy* pRenderProxy = (IEntityRenderProxy*)pEntity->GetProxy(ENTITY_PROXY_RENDER))
				{
					pRenderMat = pRenderProxy->GetRenderMaterial(entPart);

					IRenderNode* pRenderNode = pRenderProxy->GetRenderNode();
					renderFlags = pRenderNode ? pRenderNode->GetRndFlags() : 0;

					// Fall back to top level material if sub-object fails to find it
					if (!pRenderMat)
					{
						pRenderMat = pRenderProxy->GetRenderMaterial();

						if (!pRenderMat && pStatObj)
						{
							pRenderMat = pStatObj->GetMaterial();
						}
					}
				}
			}
			else if (entType == PHYS_FOREIGN_ID_STATIC)
			{
				if (IRenderNode* pBrush = (IRenderNode*)physEvent.pForeignData[PHYSEVENT_COLLIDEE])
				{
					pStatObj = pBrush->GetEntityStatObj(0, 0, &entityMat);
					pRenderMat = pBrush->GetMaterial();
					renderFlags = pBrush->GetRndFlags();

					// May need to get sub-object and it's material
					if (pStatObj && pStatObj->GetFlags() & STATIC_OBJECT_COMPOUND)
					{
						if (IStatObj::SSubObject* pSubObj = pStatObj->GetSubObject(entPart))
						{
							pStatObj = pSubObj->pStatObj;

							if (!pSubObj->bIdentityMatrix)
							{
								entityMat = entityMat * pSubObj->tm;
							}

							// Find the correct sub-material
							// Note: We loop as the slots don't always line up
							const int subMtlCount = pRenderMat->GetSubMtlCount();
							for (int i = 0; i < subMtlCount; ++i)
							{
								if (IMaterial* pSubMat = pRenderMat->GetSubMtl(i))
								{
									if (pSubMat->GetSurfaceTypeId() == initParams.surfaceTypeId)
									{
										pRenderMat = pSubMat;
										break;
									}
								}
							}
						}
					}
				}
			}
		}

		// Validate geometry of collided object
		pPhysGeom = pStatObj ? pStatObj->GetPhysGeom() : NULL;
		IGeometry* pGeom = pPhysGeom ? pPhysGeom->pGeom : NULL;
		bool validGeom = false;

		primitives::box bbox;
		int thinAxis;

		if (pGeom)
		{
			// Determine thin geometry axis for glass alignment
			pGeom->GetBBox(&bbox);
			thinAxis = idxmin3((float*)&bbox.size);

			// Handle geometry mesh type
			switch (pGeom->GetType())
			{
			case GEOM_TRIMESH:
				// Perform full mesh analysis and extraction
				if (mesh_data* pPhysMeshData = (mesh_data*)pGeom->GetData())
				{
					if (ValidatePhysMesh(pPhysMeshData, thinAxis) && ExtractPhysMesh(pPhysMeshData, thinAxis, bbox, data.defaultFrag))
					{
						validGeom = true;
					}
				}
				break;

			case GEOM_BOX:
				// Simple box, so assume valid
				validGeom = true;
				break;

			default:
				// Only support boxes and tri-meshes
				break;
			}
		}

		// Invalid geometry, so can't continue
		if (!validGeom)
		{
			pPhysGeom = NULL;
		}

		// Attempt UV coord extraction from render mesh
		else
		{	
			ExtractUVCoords(pStatObj, bbox, thinAxis, data);
		}

		// Copy final data
		data.pStatObj = pStatObj;
		data.pPhysGeom = pPhysGeom;
		data.renderFlags = renderFlags;
		data.entityMat = entityMat;
		initParams.pGlassMaterial = pRenderMat;
	}

	return data.pStatObj && data.pPhysGeom && initParams.pGlassMaterial;
}//-------------------------------------------------------------------------------------------------