123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <XRRayInteractorComponent.h>
- #include <XRInteractableComponent.h>
- #include <AzCore/Component/TransformBus.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Serialization/EditContext.h>
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <OpenXRVk/OpenXRVkActionsInterface.h>
- #include <Atom/RPI.Public/ViewProviderBus.h>
- #include <Atom/RPI.Public/View.h>
- #include <Atom/RPI.Public/ViewportContext.h>
- #include <Atom/RPI.Public/ViewportContextBus.h>
- #include <Atom/RPI.Public/Pass/PassFilter.h>
- #include <Atom/RPI.Public/ViewportContextManager.h>
- #include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
- #include <AzFramework/Components/CameraBus.h>
- #include <AzCore/Component/NonUniformScaleBus.h>
- #include <AzFramework/Physics/PhysicsScene.h>
- #include <AzFramework/Physics/PhysicsSystem.h>
- #include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
- #include <AzFramework/Physics/Shape.h>
- #include <Atom/RPI.Public/Material/Material.h>
- #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
- #include <AzCore/Name/Name.h>
- #include <AzFramework/Physics/RigidBodyBus.h>
- #include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
- namespace OpenXRVk
- {
- const AZStd::string baseColorPropertyName = "baseColor.color";
- const float translationToYScaleCoefficent = 100;
- const float hoveringXZScaleCoefficient = 4;
- void XRRayInteractorComponent::Reflect(AZ::ReflectContext* context)
- {
- if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<XRRayInteractorComponent, AZ::Component>()
- ->Version(1)
- ->Field("Max length of the ray", &XRRayInteractorComponent::m_maxLength)
- ->Field("The default color of the ray", &XRRayInteractorComponent::m_initialRayColor)
- ->Field("The color when the ray hovers over some interactable object", &XRRayInteractorComponent::m_hoveringRayColor)
- ->Field("Label of the grip button", &XRRayInteractorComponent::m_gripControlActionLabel)
- ;
- if (AZ::EditContext* editContext = serializeContext->GetEditContext())
- {
- editContext->Class<XRRayInteractorComponent>("XR Ray Interactor", "Draws ray interactor from the XR controller")
- ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::Category, "XR")
- ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
- ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
- ->DataElement(AZ::Edit::UIHandlers::Default, &XRRayInteractorComponent::m_maxLength, "Max length of the ray", "Max length of the ray in meters")
- ->DataElement(AZ::Edit::UIHandlers::Default, &XRRayInteractorComponent::m_initialRayColor, "Default color", "The default color of the ray")
- ->DataElement(AZ::Edit::UIHandlers::Default, &XRRayInteractorComponent::m_hoveringRayColor, "Hovering color", "The color when the ray hovers over some interactable object")
- ->DataElement(AZ::Edit::UIHandlers::Default, &XRRayInteractorComponent::m_gripControlActionLabel, "Action name of the grip", "Action Handle Label of OpenXRActionsInterface")
- ;
- }
- }
- if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
- {
- behaviorContext->Class<XRRayInteractorComponent>("XRRayInteractor Component Group")
- ->Attribute(AZ::Script::Attributes::Category, "OpenXRVk Gem Group")
- ;
- }
- }
- void XRRayInteractorComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
- {
- provided.push_back(AZ_CRC_CE("XRRayInteractorService"));
- }
- void XRRayInteractorComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
- {
- incompatible.push_back(AZ_CRC_CE("XRRayInteractorService"));
- }
- void XRRayInteractorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
- {
- required.push_back(AZ_CRC_CE("TransformService"));
- }
- void XRRayInteractorComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
- {
- }
- void XRRayInteractorComponent::Init()
- {
- }
- void XRRayInteractorComponent::Activate()
- {
- AZ::TickBus::Handler::BusConnect();
- }
- void XRRayInteractorComponent::Deactivate()
- {
- if (AZ::TickBus::Handler::BusIsConnected())
- {
- AZ::TickBus::Handler::BusDisconnect();
- }
- }
- void XRRayInteractorComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
- {
- if (!m_colorDefined)
- {
- AZ::Render::MaterialAssignmentMap originalMaterials;
- AZ::Render::MaterialComponentRequestBus::EventResult(
- originalMaterials, GetEntityId(), &AZ::Render::MaterialComponentRequestBus::Events::GetMaterialMap);
- if (!originalMaterials.empty())
- {
- for (const auto& [materialId, assignment] : originalMaterials)
- {
- auto materialAsset = assignment.m_materialAsset;
- if (!materialAsset.IsReady())
- continue;
- // Waiting when the asset is ready
- auto const pIndex = assignment.m_materialInstance->FindPropertyIndex(AZ::Name("baseColor.color"));
- if (!pIndex.IsValid())
- continue;
- // Let's find the right material
- assignmentId = AZ::Render::MaterialAssignmentId::CreateDefault();
- AZ::Render::MaterialComponentRequestBus::Event(
- GetEntityId(),
- &AZ::Render::MaterialComponentRequestBus::Events::SetPropertyValueT<AZ::Color>,
- assignmentId, baseColorPropertyName, m_initialRayColor);
- m_colorDefined = true;
- }
- }
- }
- ProcessOpenXRActions();
- if (m_heldEntity.IsValid())
- {
- // Update held object's position relative to ray
- AZ::Transform rayTransform;
- AZ::TransformBus::EventResult(rayTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
- AZ::Transform targetTransform = rayTransform * m_grabOffset;
- AZ::TransformBus::Event(m_heldEntity, &AZ::TransformBus::Events::SetWorldTM, targetTransform);
- // Track velocity
- AZ::Vector3 currentPosition = targetTransform.GetTranslation();
- m_currentVelocity = (currentPosition - m_lastFramePosition) / deltaTime;
- m_lastFramePosition = currentPosition;
- // Release object when button is released
- if (m_currentSqueezeValue < 0.1f)
- {
- ReleaseHeldEntity();
- }
- return;
- }
- else if (m_currentSqueezeValue > 0.9f && m_currentlyHoveredEntity.IsValid())
- {
- GrabHoveredEntity();
- return;
- }
- else {
- m_heldEntity.SetInvalid();
- }
- // Store initial nonUniform scale X and Z
- AZ::Vector3 nonUniformScale;
- AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::GetScale);
- if (m_XZnonUniformScale == 0 && nonUniformScale.GetX() > 0)
- {
- m_XZnonUniformScale = nonUniformScale.GetX();
- }
- // Get the world transform of the current entity (typically the controller)
- AZ::Transform rayOriginTransform;
- AZ::TransformBus::EventResult(rayOriginTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
- // Define the ray start position and normalized direction
- AZ::Vector3 start = rayOriginTransform.GetTranslation();
- AZ::Vector3 direction = rayOriginTransform.GetBasisY().GetNormalized();
- // Maximum ray length
- float maxDistance = m_maxLength;
- // Get the default physics scene
- auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
- AzPhysics::SceneHandle sceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
- if (sceneHandle == AzPhysics::InvalidSceneHandle)
- {
- AZ_Warning("XRRayInteractorComponent", false, "Invalid physics scene.");
- return;
- }
- // Prepare the raycast request
- AzPhysics::RayCastRequest request;
- request.m_start = start;
- request.m_direction = direction;
- request.m_distance = maxDistance;
- request.m_reportMultipleHits = false;
- // Perform the raycast query
- AzPhysics::SceneQueryHits hitResult;
- sceneInterface->QueryScene(sceneHandle, &request, hitResult);
- // Handle the result of the raycast
- if (!hitResult.m_hits.empty())
- {
- AzPhysics::SceneQueryHit &hit = hitResult.m_hits[0];
- // You can use hitResult.m_position or hitResult.m_entityId for further logic
- if (hit.m_entityId.IsValid())
- {
- if (m_currentlyHoveredEntity != hit.m_entityId)
- {
- CheckEndHovering();
- XRInteractableComponent* xrInteractableComponent = GetXRInterctableComponent(hit.m_entityId);
- if (xrInteractableComponent) // Interactable component has changed
- {
- AZ::Render::MaterialComponentRequestBus::Event(
- GetEntityId(),
- &AZ::Render::MaterialComponentRequestBus::Events::SetPropertyValueT<AZ::Color>,
- assignmentId, baseColorPropertyName, m_hoveringRayColor);
- xrInteractableComponent->OnHoverStart();
- m_currentlyHoveredEntity = hit.m_entityId;
- }
- }
- }
- else
- {
- CheckEndHovering();
- }
- float xzScale = m_currentlyHoveredEntity.IsValid() ? m_XZnonUniformScale * hoveringXZScaleCoefficient : m_XZnonUniformScale;
- nonUniformScale.Set(xzScale, hit.m_distance * translationToYScaleCoefficent, xzScale);
- AZ::NonUniformScaleRequestBus::Event(GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::SetScale, nonUniformScale);
- }
- else
- {
- // Restore initial scale of the ray
- nonUniformScale.Set(m_XZnonUniformScale, m_maxLength* translationToYScaleCoefficent, m_XZnonUniformScale);
- AZ::NonUniformScaleRequestBus::Event(GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::SetScale, nonUniformScale);
- CheckEndHovering();
- }
- }
- void XRRayInteractorComponent::CheckEndHovering()
- {
- if (m_currentlyHoveredEntity.IsValid())
- {
- XRInteractableComponent* oldCRInteractableComponent = GetXRInterctableComponent(m_currentlyHoveredEntity);
- if (oldCRInteractableComponent)
- {
- oldCRInteractableComponent->OnHoverEnd();
- m_currentlyHoveredEntity.SetInvalid();
- AZ::Render::MaterialComponentRequestBus::Event(
- GetEntityId(),
- &AZ::Render::MaterialComponentRequestBus::Events::SetPropertyValueT<AZ::Color>,
- assignmentId, baseColorPropertyName, m_initialRayColor);
- }
- }
- }
- static float ReadActionHandleFloat(IOpenXRActions* iface, IOpenXRActions::ActionHandle actionHandle, float deadZone = 0.05f)
- {
- auto outcome = iface->GetActionStateFloat(actionHandle);
- if (!outcome.IsSuccess())
- {
- // Most likely the controller went to sleep.
- return 0.0f;
- }
- float value = outcome.GetValue();
- if (fabsf(value) < deadZone)
- {
- return 0.0f;
- }
- return value;
- }
- void XRRayInteractorComponent::ProcessOpenXRActions()
- {
- m_currentSqueezeValue = 0;
- auto actionsIFace = OpenXRActionsInterface::Get();
- if (!actionsIFace)
- {
- return;
- }
- if (!m_controllerSqueezeHandle.IsValid())
- {
- // Try to cache all handles.
- m_controllerSqueezeHandle = actionsIFace->GetActionHandle("main_action_set", m_gripControlActionLabel);
- if (!m_controllerSqueezeHandle.IsValid())
- {
- // Most likely the Action System failed to load the ActionSets asset.
- return;
- }
- }
- m_currentSqueezeValue = ReadActionHandleFloat(actionsIFace, m_controllerSqueezeHandle);
- }
- void XRRayInteractorComponent::GrabHoveredEntity()
- {
- m_heldEntity = m_currentlyHoveredEntity;
- AZ::Transform controllerWorldTM;
- AZ::Transform objectWorldTM;
- AZ::TransformBus::EventResult(controllerWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
- AZ::TransformBus::EventResult(objectWorldTM, m_heldEntity, &AZ::TransformBus::Events::GetWorldTM);
- m_grabOffset = controllerWorldTM.GetInverse() * objectWorldTM;
- m_lastFramePosition = objectWorldTM.GetTranslation();
- m_currentVelocity = AZ::Vector3::CreateZero();
- XRInteractableComponent* xrInteractableComponent = GetXRInterctableComponent(m_heldEntity);
- if (xrInteractableComponent)
- {
- xrInteractableComponent->OnGrab();
- }
- }
- void XRRayInteractorComponent::ReleaseHeldEntity()
- {
- // Call OnRelease on the XRInteractableComponent if available
- XRInteractableComponent* xrInteractableComponent = GetXRInterctableComponent(m_heldEntity);
- if (xrInteractableComponent)
- {
- xrInteractableComponent->OnRelease(m_currentVelocity);
- }
- m_heldEntity.SetInvalid();
- }
- XRInteractableComponent* XRRayInteractorComponent::GetXRInterctableComponent(AZ::EntityId entityId)
- {
- AZ::Entity* entity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
- if (entity)
- {
- return entity->FindComponent<XRInteractableComponent>();
- }
- return nullptr;
- }
- } // namespace OpenXRVk
|