/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "CryLegacy_precompiled.h" #include "ComponentAudio.h" #include #include #include "Entity.h" #include #include #include "Components/IComponentArea.h" #include "Components/IComponentSerialization.h" const Audio::SATLWorldPosition CComponentAudio::s_oNULLOffset; CComponentAudio::TAudioProxyPair CComponentAudio::s_oNULLAudioProxyPair(INVALID_AUDIO_PROXY_ID, static_cast(nullptr)); ////////////////////////////////////////////////////////////////////////// inline bool IsAudioAnimEvent(const char* eventName) { return eventName && (azstricmp("audio_trigger", eventName) == 0 || azstricmp("sound", eventName) == 0); } DECLARE_DEFAULT_COMPONENT_FACTORY(CComponentAudio, IComponentAudio) ////////////////////////////////////////////////////////////////////////// CComponentAudio::CComponentAudio() : m_pEntity(nullptr) , m_nAudioProxyIDCounter(INVALID_AUDIO_PROXY_ID) , m_nAudioEnvironmentID(INVALID_AUDIO_ENVIRONMENT_ID) , m_nFlags(eEACF_CAN_MOVE_WITH_ENTITY) , m_fFadeDistance(0.0f) , m_fEnvironmentFadeDistance(0.0f) { } ////////////////////////////////////////////////////////////////////////// CComponentAudio::~CComponentAudio() { m_pEntity = nullptr; AZStd::for_each(m_mapAuxAudioProxies.begin(), m_mapAuxAudioProxies.end(), SReleaseAudioProxy()); m_mapAuxAudioProxies.clear(); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::Initialize(const SComponentInitializer& init) { m_pEntity = init.m_pEntity; m_pEntity->GetComponent()->Register(SerializationOrder::Audio, *this, &CComponentAudio::Serialize, &CComponentAudio::SerializeXML, &CComponentAudio::NeedSerialize, &CComponentAudio::GetSignature); assert(m_mapAuxAudioProxies.empty()); // Creating the default AudioProxy. CreateAuxAudioProxy(); UpdateHideStatus(); SetObstructionCalcType(Audio::eAOOCT_IGNORE); OnMove(); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::Reload(IEntity* pEntity, SEntitySpawnParams& params) { m_pEntity = pEntity; m_fFadeDistance = 0.0f; m_fEnvironmentFadeDistance = 0.0f; m_nAudioEnvironmentID = INVALID_AUDIO_ENVIRONMENT_ID; UpdateHideStatus(); AZStd::for_each(m_mapAuxAudioProxies.begin(), m_mapAuxAudioProxies.end(), SResetAudioProxy()); #if defined(INCLUDE_ENTITYSYSTEM_PRODUCTION_CODE) AZStd::for_each(m_mapAuxAudioProxies.begin(), m_mapAuxAudioProxies.end(), SInitializeAudioProxy(m_pEntity->GetName())); #else AZStd::for_each(m_mapAuxAudioProxies.begin(), m_mapAuxAudioProxies.end(), SInitializeAudioProxy(nullptr)); #endif // INCLUDE_ENTITYSYSTEM_PRODUCTION_CODE SetObstructionCalcType(Audio::eAOOCT_IGNORE); OnMove(); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnMove() { const Matrix34& tm = m_pEntity->GetWorldTM(); if (tm.IsValid()) { const Audio::SATLWorldPosition oPosition(LYTransformToAZTransform(tm)); // Update all proxies with the eEACF_CAN_MOVE_WITH_ENTITY flag set. AZStd::for_each(m_mapAuxAudioProxies.begin(), m_mapAuxAudioProxies.end(), SRepositionAudioProxy(oPosition)); if ((m_pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { Audio::SAudioRequest oRequest; // Don't set the audio object ID, this will go to the default listener unless // the listener is being overridden by another. oRequest.nFlags = Audio::eARF_PRIORITY_NORMAL; oRequest.pOwner = this; Audio::SAudioListenerRequestData oRequestData(oPosition); // Make sure direction and up vector are normalized oRequestData.oNewPosition.NormalizeForwardVec(); oRequestData.oNewPosition.NormalizeUpVec(); oRequest.pData = &oRequestData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oRequest); // As this is an audio listener add its entity to the AreaManager for raising audio relevant events. gEnv->pEntitySystem->GetAreaManager()->MarkEntityForUpdate(m_pEntity->GetId()); } } // Audio: voice attachement lookup needs to be done by code creating CComponentAudio for voice line on character //if (m_nAttachmentIndex != -1) //{ // if (ICharacterInstance* const pCharacter = m_pEntity->GetCharacter(0)) // { // if (m_nAttachmentIndex != -1) // { // if (IAttachmentManager const* const pAttachmentManager = pCharacter->GetIAttachmentManager()) // { // if (IAttachment const* const pAttachment = pAttachmentManager->GetInterfaceByIndex(m_nAttachmentIndex)) // { // tm = Matrix34(pAttachment->GetAttWorldAbsolute()); // } // } // } // else if (m_nBoneHead != -1) // { // // re-query SkeletonPose to prevent crash on removed Character // if (ISkeletonPose* const pSkeletonPose = pCharacter->GetISkeletonPose()) // { // tm = tm * Matrix34(pSkeletonPose->GetAbsJointByID(m_nBoneHead)); // } // } // } //} } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnListenerMoveInside(const IEntity* const pEntity) { m_pEntity->SetPos(pEntity->GetWorldPos()); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnMoveInside(const IEntity* const pEntity) { } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnListenerExclusiveMoveInside(const IEntity* const __restrict pEntity, const IEntity* const __restrict pAreaHigh, const IEntity* const __restrict pAreaLow, const float fFade) { const IComponentArea* const __restrict pAreaComponentLow = pAreaLow->GetComponent().get(); IComponentArea* const __restrict pAreaComponentHigh = const_cast(pAreaHigh->GetComponent().get()); if (pAreaComponentLow && pAreaComponentHigh) { Vec3 OnHighHull3d(ZERO); const Vec3 oPos(pEntity->GetWorldPos()); const EntityId nEntityID = pEntity->GetId(); const bool bInsideLow = pAreaComponentLow->CalcPointWithin(nEntityID, oPos); if (bInsideLow) { const float fDistSq = pAreaComponentHigh->ClosestPointOnHullDistSq(nEntityID, oPos, OnHighHull3d); m_pEntity->SetPos(OnHighHull3d); } } } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnExclusiveMoveInside(const IEntity* const __restrict pEntity, const IEntity* const __restrict pEntityAreaHigh, const IEntity* const __restrict pEntityAreaLow, const float fEnvironmentFade) { SetEnvironmentAmountInternal(pEntity, fEnvironmentFade); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnListenerEnter(const IEntity* const pEntity) { m_pEntity->SetPos(pEntity->GetWorldPos()); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnEnter(const IEntity* const pEntity) { SetEnvironmentAmountInternal(pEntity, 1.0f); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnLeaveNear(const IEntity* const pEntity) { SetEnvironmentAmountInternal(pEntity, 0.0f); } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnAreaCrossing() { } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnListenerMoveNear(const IEntity* const __restrict pEntity, const IEntity* const __restrict pArea) { IComponentArea* const pAreaComponent = const_cast(pArea->GetComponent().get()); if (pAreaComponent) { Vec3 OnHull3d(ZERO); const float fDistSq = pAreaComponent->CalcPointNearDistSq(pEntity->GetId(), pEntity->GetWorldPos(), OnHull3d); m_pEntity->SetPos(OnHull3d); } } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::OnMoveNear(const IEntity* const __restrict pEntity, const IEntity* const __restrict pArea) { if (m_nAudioEnvironmentID == INVALID_AUDIO_ENVIRONMENT_ID) { return; } IComponentAudio* const pAudioComponent = const_cast(pEntity->GetComponent().get()); IComponentArea* const pAreaComponent = const_cast(pArea->GetComponent().get()); if (pAudioComponent && pAreaComponent && (m_nAudioEnvironmentID != INVALID_AUDIO_ENVIRONMENT_ID)) { // Only set the Audio Environment Amount on the entities that already have an AudioProxy. // Passing INVALID_AUDIO_PROXY_ID to address all auxiliary AudioProxies on pAudioComponent. Vec3 OnHull3d(ZERO); const float fDist = sqrt(pAreaComponent->CalcPointNearDistSq(pEntity->GetId(), pEntity->GetWorldPos(), OnHull3d)); if ((fDist > m_fEnvironmentFadeDistance) || (m_fEnvironmentFadeDistance < FLT_EPSILON)) { pAudioComponent->SetEnvironmentAmount(m_nAudioEnvironmentID, 0.0f, INVALID_AUDIO_PROXY_ID); } else { pAudioComponent->SetEnvironmentAmount(m_nAudioEnvironmentID, 1.0f - (fDist / m_fEnvironmentFadeDistance), INVALID_AUDIO_PROXY_ID); } } } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::ProcessEvent(SEntityEvent& event) { if (m_pEntity) { switch (event.event) { case ENTITY_EVENT_XFORM: { const int nFlags = (int)event.nParam[0]; if ((nFlags & (ENTITY_XFORM_POS | ENTITY_XFORM_ROT)) != 0) { OnMove(); } break; } case ENTITY_EVENT_ENTERAREA: { if ((m_pEntity->GetFlags() & ENTITY_FLAG_VOLUME_SOUND) != 0) { const auto nEntityID = static_cast(event.nParam[0]); // Entering entity! IEntity* const pEntity = gEnv->pEntitySystem->GetEntity(nEntityID); if (pEntity && (pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { OnListenerEnter(pEntity); } else if (pEntity && (m_nAudioEnvironmentID != INVALID_AUDIO_ENVIRONMENT_ID)) { OnEnter(pEntity); } } break; } case ENTITY_EVENT_LEAVEAREA: { break; } case ENTITY_EVENT_CROSS_AREA: { if ((m_pEntity->GetFlags() & ENTITY_FLAG_VOLUME_SOUND) != 0) { const auto nEntityID = static_cast(event.nParam[0]); // Crossing entity! IEntity* const pEntity = gEnv->pEntitySystem->GetEntity(nEntityID); if (pEntity && (pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { OnAreaCrossing(); } } break; } case ENTITY_EVENT_MOVENEARAREA: case ENTITY_EVENT_ENTERNEARAREA: { if ((m_pEntity->GetFlags() & ENTITY_FLAG_VOLUME_SOUND) != 0) { const auto nEntityID = static_cast(event.nParam[0]); // Near entering/moving entity! IEntity* const pEntity = gEnv->pEntitySystem->GetEntity(nEntityID); const auto nAreaEntityID = static_cast(event.nParam[2]); IEntity* const pArea = gEnv->pEntitySystem->GetEntity(nAreaEntityID); if (pEntity && pArea) { if ((pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { // This entity is an audio listener. OnListenerMoveNear(pEntity, pArea); } else if (m_nAudioEnvironmentID != INVALID_AUDIO_ENVIRONMENT_ID) { OnMoveNear(pEntity, pArea); } } } break; } case ENTITY_EVENT_LEAVENEARAREA: { if ((m_pEntity->GetFlags() & ENTITY_FLAG_VOLUME_SOUND) != 0) { const auto nEntityID = static_cast(event.nParam[0]); // Leaving entity! IEntity* const pEntity = gEnv->pEntitySystem->GetEntity(nEntityID); if (pEntity && (pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) == 0) { OnLeaveNear(pEntity); } } break; } case ENTITY_EVENT_MOVEINSIDEAREA: { if ((m_pEntity->GetFlags() & ENTITY_FLAG_VOLUME_SOUND) != 0) { const auto nEntityID = static_cast(event.nParam[0]); // Inside moving entity! IEntity* const __restrict pEntity = gEnv->pEntitySystem->GetEntity(nEntityID); if (pEntity) { const auto nAreaID1 = static_cast(event.nParam[2]); // AreaEntityID (low) const auto nAreaID2 = static_cast(event.nParam[3]); // AreaEntityID (high) IEntity* const __restrict pArea1 = gEnv->pEntitySystem->GetEntity(nAreaID1); IEntity* const __restrict pArea2 = gEnv->pEntitySystem->GetEntity(nAreaID2); if (pArea1) { if (pArea2) { if ((pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { OnListenerExclusiveMoveInside(pEntity, pArea2, pArea1, event.fParam[0]); } else { OnExclusiveMoveInside(pEntity, pArea2, pArea1, event.fParam[1]); } } else { if ((pEntity->GetFlagsExtended() & ENTITY_FLAG_EXTENDED_AUDIO_LISTENER) != 0) { OnListenerMoveInside(pEntity); } else { OnMoveInside(pEntity); } } } } } break; } case ENTITY_EVENT_ANIM_EVENT: { auto pAnimEvent = reinterpret_cast(event.nParam[0]); auto pCharacter = reinterpret_cast(event.nParam[1]); const char* eventName = (pAnimEvent ? pAnimEvent->m_EventName : nullptr); if (IsAudioAnimEvent(eventName)) { Vec3 offset(ZERO); if (pAnimEvent->m_BonePathName && pAnimEvent->m_BonePathName[0]) { if (pCharacter) { IDefaultSkeleton& rIDefaultSkeleton = pCharacter->GetIDefaultSkeleton(); int id = rIDefaultSkeleton.GetJointIDByName(pAnimEvent->m_BonePathName); if (id >= 0) { ISkeletonPose* pSkeletonPose = pCharacter->GetISkeletonPose(); QuatT boneQuat(pSkeletonPose->GetAbsJointByID(id)); offset = boneQuat.t; } } } Audio::TAudioControlID controlId = INVALID_AUDIO_CONTROL_ID; Audio::AudioSystemRequestBus::BroadcastResult(controlId, &Audio::AudioSystemRequestBus::Events::GetAudioTriggerID, pAnimEvent->m_CustomParameter); if (controlId != INVALID_AUDIO_CONTROL_ID) { ExecuteTrigger(controlId, eLSM_None); } } break; } case ENTITY_EVENT_HIDE: case ENTITY_EVENT_UNHIDE: { UpdateHideStatus(); break; } } } } ////////////////////////////////////////////////////////////////////////// bool CComponentAudio::GetSignature(TSerialize signature) { // CComponentAudio is not relevant to signature as it is always created again if needed return true; } ////////////////////////////////////////////////////////////////////////// void CComponentAudio::Serialize(TSerialize ser) { } ////////////////////////////////////////////////////////////////////////// bool CComponentAudio::ExecuteTrigger( const Audio::TAudioControlID nTriggerID, const ELipSyncMethod eLipSyncMethod, const Audio::TAudioProxyID nAudioProxyLocalID /* = DEFAULT_AUDIO_PROXY_ID */, const Audio::SAudioCallBackInfos& rCallBackInfos /* = SAudioCallBackInfos::GetEmptyObject() */) { if (m_pEntity) { const Matrix34& tm = m_pEntity->GetWorldTM(); #if defined(INCLUDE_ENTITYSYSTEM_PRODUCTION_CODE) if (tm.GetTranslation() == Vec3_Zero) { gEnv->pSystem->Warning(VALIDATOR_MODULE_ENTITYSYSTEM, VALIDATOR_WARNING, VALIDATOR_FLAG_AUDIO, 0, "