AkComponentHelpers.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "AkComponentHelpers.h"
  16. #include "Components/PrimitiveComponent.h"
  17. #include "AkAudioDevice.h"
  18. #include "GameFramework/WorldSettings.h"
  19. namespace AkComponentHelpers
  20. {
  21. bool HasSimpleCollisionGeometry(const UBodySetup* bodySetup)
  22. {
  23. FKAggregateGeom geometry = bodySetup->AggGeom;
  24. return geometry.BoxElems.Num() > 0 || geometry.ConvexElems.Num() > 0 || geometry.SphereElems.Num() > 0 || geometry.TaperedCapsuleElems.Num() > 0 || geometry.SphylElems.Num() > 0;
  25. }
  26. bool EncompassesPoint(UPrimitiveComponent& Primitive, FVector Point, float SphereRadius /*= 0.f*/, float* OutDistanceToPoint /*= nullptr*/)
  27. {
  28. bool bUsePhysicsCollision = Primitive.GetOwner() != nullptr;
  29. #ifndef WITH_PHYSX
  30. bUsePhysicsCollision = false;
  31. #endif
  32. float DistanceSqr = 0.0f;
  33. const UBodySetup* bodySetup = Primitive.GetBodySetup();
  34. if (bodySetup == nullptr || !AkComponentHelpers::HasSimpleCollisionGeometry(bodySetup))
  35. {
  36. bUsePhysicsCollision = false;
  37. }
  38. if (bUsePhysicsCollision)
  39. {
  40. FVector ClosestPoint;
  41. if (Primitive.GetSquaredDistanceToCollision(Point, DistanceSqr, ClosestPoint) == false)
  42. {
  43. if (OutDistanceToPoint)
  44. {
  45. *OutDistanceToPoint = -1.f;
  46. }
  47. return false;
  48. }
  49. }
  50. else
  51. {
  52. // Just use simple bounds intersection. This will be less accurate, especially for complex-shaped non-box rooms.
  53. // Since bUseAttachParentBound = true, we can use Bounds here.
  54. DistanceSqr = Primitive.Bounds.GetBox().ComputeSquaredDistanceToPoint(Point);
  55. }
  56. if (OutDistanceToPoint)
  57. {
  58. *OutDistanceToPoint = FMath::Sqrt(DistanceSqr);
  59. }
  60. return DistanceSqr >= 0.f && DistanceSqr <= FMath::Square(SphereRadius);
  61. }
  62. float UnrealUnitsPerMeter(const UActorComponent* component)
  63. {
  64. const float defaultWorldToMetersRatio = 100.0f;
  65. if (component == nullptr)
  66. return defaultWorldToMetersRatio;
  67. if (component->GetWorld() == nullptr)
  68. return defaultWorldToMetersRatio;
  69. AWorldSettings* settings = component->GetWorld()->GetWorldSettings();
  70. if (settings == nullptr)
  71. return defaultWorldToMetersRatio;
  72. return settings->WorldToMeters;
  73. }
  74. float UnrealUnitsPerSquaredMeter(const UActorComponent* component)
  75. {
  76. return FMath::Pow(UnrealUnitsPerMeter(component), 2.0f);
  77. }
  78. float UnrealUnitsPerCubicMeter(const UActorComponent* component)
  79. {
  80. return FMath::Pow(UnrealUnitsPerMeter(component), 3.0f);
  81. }
  82. bool IsInGameWorld(const UActorComponent* InComponent)
  83. {
  84. UWorld* World = InComponent->GetWorld();
  85. if (World == nullptr)
  86. return false;
  87. return World->WorldType == EWorldType::Game || World->WorldType == EWorldType::PIE;
  88. }
  89. AKAUDIO_API FBoxSphereBounds GetPrimitiveBoundsNoRotation(const UPrimitiveComponent& Primitive)
  90. {
  91. FTransform Transform(Primitive.GetComponentTransform());
  92. Transform.SetRotation(FQuat::Identity);
  93. return Primitive.CalcBounds(Transform);
  94. }
  95. void GetPrimitiveTransformAndExtent(const UPrimitiveComponent& Primitive, AkWorldTransform& transform, AkExtent& extent)
  96. {
  97. FRotator rotation = Primitive.GetComponentRotation();
  98. FVector front = rotation.RotateVector(FVector::ForwardVector);
  99. FVector up = rotation.RotateVector(FVector::UpVector);
  100. AkVector Front, Up;
  101. FAkAudioDevice::FVectorToAKVector(front, Front);
  102. FAkAudioDevice::FVectorToAKVector(up, Up);
  103. transform.SetOrientation(Front, Up);
  104. FBoxSphereBounds primitiveBounds = GetPrimitiveBoundsNoRotation(Primitive);
  105. extent = FAkAudioDevice::FVectorToAkExtent(primitiveBounds.BoxExtent); // Potential loss of precision here
  106. AkVector64 Center;
  107. // For uniformly shaped primitives, primitiveBounds.Origin will be the same as the component position.
  108. // For complex meshes and brushes, there will be an offset.
  109. FAkAudioDevice::FVectorToAKVector64(Primitive.Bounds.Origin, Center);
  110. transform.SetPosition(Center);
  111. }
  112. void GetPrimitiveUpAndFront(const UPrimitiveComponent& Primitive, AkVector& Up, AkVector& Front)
  113. {
  114. FRotator rotation = Primitive.GetComponentRotation();
  115. FVector front = rotation.RotateVector(FVector::ForwardVector);
  116. FVector up = rotation.RotateVector(FVector::UpVector);
  117. FAkAudioDevice::FVectorToAKVector(front, Front);
  118. FAkAudioDevice::FVectorToAKVector(up, Up);
  119. }
  120. bool DoesMovementRecenterChild(USceneComponent* child, USceneComponent* parent, const FVector& Delta)
  121. {
  122. #if WITH_EDITOR
  123. // Only implement movement when it is to recentre component at local origin
  124. FVector direction;
  125. float length;
  126. FVector relativeDestination = child->GetRelativeLocation() + Delta;
  127. // When movement happens from OnRegister, after reparenting, GetRelativeLocation() can sometimes refer to the old parent,
  128. // because the private SceneComponent::RelativeLocation member has not been correctly updated yet for this frame.
  129. // This causes a local offset to be left applied to the component after it is reparented.
  130. // Therefore, use Parent, which will refer to the current attachment parent and use it to derive the correct relative location.
  131. if (parent != nullptr)
  132. {
  133. relativeDestination = (child->GetComponentLocation() + Delta) - parent->GetComponentLocation();
  134. }
  135. relativeDestination.ToDirectionAndLength(direction, length);
  136. if (length <= 0.01f)
  137. return true;
  138. #endif //WITH_EDITOR
  139. return false;
  140. }
  141. void LogAttachmentError(const UActorComponent* child, const UActorComponent* parent, const FString& requiredClassName)
  142. {
  143. FString actorString = FString("NONE");
  144. if (child->GetOwner() != nullptr)
  145. actorString = child->GetOwner()->GetName();
  146. FString parentName = parent->GetName();
  147. FString parentClass = parent->GetClass()->GetName();
  148. FString childName = child->GetName();
  149. FString childClass = child->GetClass()->GetName();
  150. UE_LOG(LogAkAudio, Error,
  151. TEXT("On actor %s, there is a %s (%s) attached to parent of type %s (%s)."),
  152. *actorString, *childClass, *childName, *parentClass, *parentName);
  153. UE_LOG(LogAkAudio, Error, TEXT("%s requires to be nested under a component inheriting from %s."),
  154. *childClass, *requiredClassName);
  155. }
  156. void LogSimpleGeometryWarning(const UPrimitiveComponent* parent, const UActorComponent* child)
  157. {
  158. FString primitiveName = "";
  159. FString childName = "";
  160. parent->GetName(primitiveName);
  161. child->GetName(childName);
  162. FString actorName = "";
  163. AActor* owner = parent->GetOwner();
  164. if (owner != nullptr)
  165. owner->GetName(actorName);
  166. UE_LOG(LogAkAudio, Warning,
  167. TEXT("Primitive component %s on actor %s has no simple collision geometry.%sChild component %s will use component bounds for containment calculations. This could be less accurate than using simple collision geometry."),
  168. *primitiveName, *actorName, LINE_TERMINATOR, *childName);
  169. }
  170. #if WITH_EDITOR
  171. bool IsGameWorldBlueprintComponent(const UActorComponent* InComponent)
  172. {
  173. // CreationMethod == EComponentCreationMethod::SimpleConstructionScript means the component was added as part of a blueprint class.
  174. return IsInGameWorld(InComponent) && InComponent->CreationMethod == EComponentCreationMethod::SimpleConstructionScript;
  175. }
  176. bool ShouldDeferBeginPlay(const UActorComponent* InComponent)
  177. {
  178. bool worldHasBegunPlay = InComponent->GetWorld() && InComponent->GetWorld()->HasBegunPlay();
  179. return IsGameWorldBlueprintComponent(InComponent) && worldHasBegunPlay;
  180. }
  181. #endif
  182. }