ScreenToWorld and WorldToScreen Camera functionality (#6903)
Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com>
This commit is contained in:
@@ -129,6 +129,32 @@ namespace Camera
|
||||
GetFrustumHeight()
|
||||
};
|
||||
}
|
||||
|
||||
//! Unprojects a position in screen space pixel coordinates to world space.
|
||||
//! With a depth of zero, the position returned will be on the near clip plane of the camera
|
||||
//! in world space.
|
||||
//! @param screenPosition The absolute screen position
|
||||
//! @param depth The depth offset into the world relative to the near clip plane of the camera
|
||||
//! @return the position in world space
|
||||
virtual AZ::Vector3 ScreenToWorld(const AZ::Vector2& screenPosition, float depth) = 0;
|
||||
|
||||
//! Unprojects a position in screen space normalized device coordinates to world space.
|
||||
//! With a depth of zero, the position returned will be on the near clip plane of the camera
|
||||
//! in world space.
|
||||
//! @param screenNdcPosition The normalized device coordinates in the range [0,1]
|
||||
//! @param depth The depth offset into the world relative to the near clip plane of the camera
|
||||
//! @return the position in world space
|
||||
virtual AZ::Vector3 ScreenNdcToWorld(const AZ::Vector2& screenNdcPosition, float depth) = 0;
|
||||
|
||||
//! Projects a position in world space to screen space for the given camera.
|
||||
//! @param worldPosition The world position
|
||||
//! @return The absolute screen position
|
||||
virtual AZ::Vector2 WorldToScreen(const AZ::Vector3& worldPosition) = 0;
|
||||
|
||||
//! Projects a position in world space to screen space normalized device coordinates.
|
||||
//! @param worldPosition The world position
|
||||
//! @return The normalized device coordinates in the range [0,1]
|
||||
virtual AZ::Vector2 WorldToScreenNdc(const AZ::Vector3& worldPosition) = 0;
|
||||
};
|
||||
using CameraRequestBus = AZ::EBus<CameraComponentRequests>;
|
||||
|
||||
|
||||
+4
@@ -104,6 +104,10 @@ namespace AZ
|
||||
void SetOrthographicHalfWidth(float halfWidth) override;
|
||||
void MakeActiveView() override;
|
||||
bool IsActiveView() override;
|
||||
AZ::Vector3 ScreenToWorld(const AZ::Vector2& screenPosition, float depth) override;
|
||||
AZ::Vector3 ScreenNdcToWorld(const AZ::Vector2& screenPosition, float depth) override;
|
||||
AZ::Vector2 WorldToScreen(const AZ::Vector3& worldPosition) override;
|
||||
AZ::Vector2 WorldToScreenNdc(const AZ::Vector3& worldPosition) override;
|
||||
|
||||
// RPI::WindowContextNotificationBus overrides...
|
||||
void OnViewportResized(uint32_t width, uint32_t height) override;
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace AZ
|
||||
AZ_Assert(false, "DebugCamera does not support orthographic projection");
|
||||
}
|
||||
|
||||
void CameraComponent::MakeActiveView()
|
||||
void CameraComponent::MakeActiveView()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
@@ -255,6 +255,30 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::Vector3 CameraComponent::ScreenToWorld([[maybe_unused]] const AZ::Vector2& screenPosition, [[maybe_unused]] float depth)
|
||||
{
|
||||
// not implemented
|
||||
return AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector3 CameraComponent::ScreenNdcToWorld([[maybe_unused]] const AZ::Vector2& screenPosition, [[maybe_unused]] float depth)
|
||||
{
|
||||
// not implemented
|
||||
return AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector2 CameraComponent::WorldToScreen([[maybe_unused]] const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// not implemented
|
||||
return AZ::Vector2::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector2 CameraComponent::WorldToScreenNdc([[maybe_unused]] const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// not implemented
|
||||
return AZ::Vector2::CreateZero();
|
||||
}
|
||||
|
||||
void CameraComponent::OnViewportResized(uint32_t width, uint32_t height)
|
||||
{
|
||||
AZ_UNUSED(width);
|
||||
|
||||
@@ -106,6 +106,10 @@ namespace Camera
|
||||
->Event("SetOrthographic", &CameraRequestBus::Events::SetOrthographic)
|
||||
->Event("GetOrthographicHalfWidth", &CameraRequestBus::Events::GetOrthographicHalfWidth)
|
||||
->Event("SetOrthographicHalfWidth", &CameraRequestBus::Events::SetOrthographicHalfWidth)
|
||||
->Event("ScreenToWorld", &CameraRequestBus::Events::ScreenToWorld)
|
||||
->Event("ScreenNdcToWorld", &CameraRequestBus::Events::ScreenNdcToWorld)
|
||||
->Event("WorldToScreen", &CameraRequestBus::Events::WorldToScreen)
|
||||
->Event("WorldToScreenNdc", &CameraRequestBus::Events::WorldToScreenNdc)
|
||||
->VirtualProperty("FieldOfView","GetFovDegrees","SetFovDegrees")
|
||||
->VirtualProperty("NearClipDistance", "GetNearClipDistance", "SetNearClipDistance")
|
||||
->VirtualProperty("FarClipDistance", "GetFarClipDistance", "SetFarClipDistance")
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
#include "CameraViewRegistrationBus.h"
|
||||
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/Math/VectorConversions.h>
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <Atom/RPI.Public/ViewportContextManager.h>
|
||||
#include <Atom/RPI.Public/ViewportContext.h>
|
||||
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
namespace Camera
|
||||
{
|
||||
void CameraComponentConfig::Reflect(AZ::ReflectContext* context)
|
||||
@@ -412,6 +415,66 @@ namespace Camera
|
||||
return m_isActiveView;
|
||||
}
|
||||
|
||||
namespace Util
|
||||
{
|
||||
AZ::Vector3 GetWorldPosition(const AZ::Vector3& origin, float depth, const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
if (depth == 0.f)
|
||||
{
|
||||
return origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
const AZ::Vector3 rayDirection = cameraState.m_orthographic ? cameraState.m_forward : (origin - cameraState.m_position);
|
||||
return origin + (rayDirection.GetNormalized() * depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 CameraComponentController::ScreenToWorld(const AZ::Vector2& screenPosition, float depth)
|
||||
{
|
||||
const AzFramework::ScreenPoint point{ static_cast<int>(screenPosition.GetX()), static_cast<int>(screenPosition.GetY()) };
|
||||
const AzFramework::CameraState& cameraState = GetCameraState();
|
||||
const AZ::Vector3 origin = AzFramework::ScreenToWorld(point, cameraState);
|
||||
return Util::GetWorldPosition(origin, depth, cameraState);
|
||||
}
|
||||
|
||||
AZ::Vector3 CameraComponentController::ScreenNdcToWorld(const AZ::Vector2& screenNdcPosition, float depth)
|
||||
{
|
||||
const AzFramework::CameraState& cameraState = GetCameraState();
|
||||
const AZ::Vector3 origin = AzFramework::ScreenNdcToWorld(screenNdcPosition, AzFramework::InverseCameraView(cameraState), AzFramework::InverseCameraProjection(cameraState));
|
||||
return Util::GetWorldPosition(origin, depth, cameraState);
|
||||
}
|
||||
|
||||
AZ::Vector2 CameraComponentController::WorldToScreenNdc(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
const AzFramework::CameraState& cameraState = GetCameraState();
|
||||
const AZ::Vector3 screenPosition = AzFramework::WorldToScreenNdc(worldPosition, AzFramework::CameraView(cameraState), AzFramework::CameraProjection(cameraState));
|
||||
return AZ::Vector3ToVector2(screenPosition);
|
||||
}
|
||||
|
||||
AZ::Vector2 CameraComponentController::WorldToScreen(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
const AzFramework::ScreenPoint& point = AzFramework::WorldToScreen(worldPosition, GetCameraState());
|
||||
return AZ::Vector2(static_cast<float>(point.m_x), static_cast<float>(point.m_y));
|
||||
}
|
||||
|
||||
AzFramework::CameraState CameraComponentController::GetCameraState()
|
||||
{
|
||||
auto viewportContext = GetViewportContext();
|
||||
if (!m_atomCamera || ! viewportContext)
|
||||
{
|
||||
return AzFramework::CameraState();
|
||||
}
|
||||
|
||||
auto windowSize = viewportContext->GetViewportSize();
|
||||
auto viewportSize = AzFramework::Vector2FromScreenSize(AzFramework::ScreenSize(windowSize.m_width, windowSize.m_height));
|
||||
|
||||
AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(m_atomCamera->GetCameraTransform(), viewportSize);
|
||||
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, m_atomCamera->GetViewToClipMatrix());
|
||||
return cameraState;
|
||||
}
|
||||
|
||||
void CameraComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
|
||||
{
|
||||
if (m_updatingTransformFromEntity)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
#include <Atom/RPI.Public/ViewProviderBus.h>
|
||||
@@ -110,6 +111,11 @@ namespace Camera
|
||||
void MakeActiveView() override;
|
||||
bool IsActiveView() override;
|
||||
|
||||
AZ::Vector3 ScreenToWorld(const AZ::Vector2& screenPosition, float depth) override;
|
||||
AZ::Vector3 ScreenNdcToWorld(const AZ::Vector2& screenNdcPosition, float depth) override;
|
||||
AZ::Vector2 WorldToScreen(const AZ::Vector3& worldPosition) override;
|
||||
AZ::Vector2 WorldToScreenNdc(const AZ::Vector3& worldPosition) override;
|
||||
|
||||
// AZ::TransformNotificationBus::Handler interface
|
||||
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
|
||||
|
||||
@@ -127,6 +133,7 @@ namespace Camera
|
||||
void DeactivateAtomView();
|
||||
void UpdateCamera();
|
||||
void SetupAtomAuxGeom(AZ::RPI::ViewportContextPtr viewportContext);
|
||||
AzFramework::CameraState GetCameraState();
|
||||
|
||||
CameraComponentConfig m_config;
|
||||
AZ::EntityId m_entityId;
|
||||
|
||||
@@ -31,6 +31,66 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "WorldToScreen",
|
||||
"entry": {
|
||||
"name": "In",
|
||||
"tooltip": "When signaled, this will invoke World To Screen"
|
||||
},
|
||||
"exit": {
|
||||
"name": "Out",
|
||||
"tooltip": "Signaled after World To Screen is invoked"
|
||||
},
|
||||
"details": {
|
||||
"name": "World To Screen"
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"typeid": "{8379EB7D-01FA-4538-B64B-A6543B4BE73D}",
|
||||
"details": {
|
||||
"name": "World Position"
|
||||
}
|
||||
}
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"typeid": "{3D80F623-C85C-4741-90D0-E4E66164E6BF}",
|
||||
"details": {
|
||||
"name": "Screen Position"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "WorldToScreenNdc",
|
||||
"entry": {
|
||||
"name": "In",
|
||||
"tooltip": "When signaled, this will invoke World To Screen Ndc"
|
||||
},
|
||||
"exit": {
|
||||
"name": "Out",
|
||||
"tooltip": "Signaled after World To Screen Ndc is invoked"
|
||||
},
|
||||
"details": {
|
||||
"name": "World To Screen NDC"
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"typeid": "{8379EB7D-01FA-4538-B64B-A6543B4BE73D}",
|
||||
"details": {
|
||||
"name": "World Position"
|
||||
}
|
||||
}
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"typeid": "{3D80F623-C85C-4741-90D0-E4E66164E6BF}",
|
||||
"details": {
|
||||
"name": "Screen NDC Position"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "GetFov",
|
||||
"entry": {
|
||||
@@ -53,6 +113,78 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "ScreenToWorld",
|
||||
"entry": {
|
||||
"name": "In",
|
||||
"tooltip": "When signaled, this will invoke Screen To World"
|
||||
},
|
||||
"exit": {
|
||||
"name": "Out",
|
||||
"tooltip": "Signaled after Screen To World is invoked"
|
||||
},
|
||||
"details": {
|
||||
"name": "Screen To World"
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"typeid": "{3D80F623-C85C-4741-90D0-E4E66164E6BF}",
|
||||
"details": {
|
||||
"name": "Screen Position"
|
||||
}
|
||||
},
|
||||
{
|
||||
"typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}",
|
||||
"details": {
|
||||
"name": "Depth"
|
||||
}
|
||||
}
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"typeid": "{8379EB7D-01FA-4538-B64B-A6543B4BE73D}",
|
||||
"details": {
|
||||
"name": "World Position"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "ScreenNdcToWorld",
|
||||
"entry": {
|
||||
"name": "In",
|
||||
"tooltip": "When signaled, this will invoke Screen Ndc To World"
|
||||
},
|
||||
"exit": {
|
||||
"name": "Out",
|
||||
"tooltip": "Signaled after Screen Ndc To World is invoked"
|
||||
},
|
||||
"details": {
|
||||
"name": "Screen NDC To World"
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"typeid": "{3D80F623-C85C-4741-90D0-E4E66164E6BF}",
|
||||
"details": {
|
||||
"name": "Screen NDC Position"
|
||||
}
|
||||
},
|
||||
{
|
||||
"typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}",
|
||||
"details": {
|
||||
"name": "Depth"
|
||||
}
|
||||
}
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"typeid": "{8379EB7D-01FA-4538-B64B-A6543B4BE73D}",
|
||||
"details": {
|
||||
"name": "World Position"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"base": "SetFovRadians",
|
||||
"entry": {
|
||||
@@ -356,4 +488,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"base": "{B164D87F-6620-5A1D-A2BC-CC09BA18C9B1}",
|
||||
"context": "ScriptCanvas::Node",
|
||||
"variant": "",
|
||||
"details": {
|
||||
"name": "Ray Cast From Screen With Group",
|
||||
"category": "PhysX/World",
|
||||
"tooltip": "Returns the first entity hit by a ray cast from the provided absolute 2D screen position."
|
||||
},
|
||||
"slots": [
|
||||
{
|
||||
"base": "Input_In_0",
|
||||
"details": {
|
||||
"name": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "Output_Out_0",
|
||||
"details": {
|
||||
"name": "Out"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataInput_Screen Position_0",
|
||||
"details": {
|
||||
"name": "Screen Position"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataInput_Distance_1",
|
||||
"details": {
|
||||
"name": "Distance"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataInput_Collision group_2",
|
||||
"details": {
|
||||
"name": "Collision group"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataInput_Ignore_3",
|
||||
"details": {
|
||||
"name": "Ignore"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_Object hit_0",
|
||||
"details": {
|
||||
"name": "Object hit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_Position_1",
|
||||
"details": {
|
||||
"name": "Position"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_Normal_2",
|
||||
"details": {
|
||||
"name": "Normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_Distance_3",
|
||||
"details": {
|
||||
"name": "Distance"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_EntityId_4",
|
||||
"details": {
|
||||
"name": "EntityId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"base": "DataOutput_Surface_5",
|
||||
"details": {
|
||||
"name": "Surface"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <AzFramework/Physics/Collision/CollisionGroups.h>
|
||||
#include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
|
||||
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
@@ -120,6 +121,43 @@ namespace ScriptCanvasPhysics
|
||||
"EntityId",
|
||||
"Surface");
|
||||
|
||||
AZ_INLINE Result RayCastFromScreenWithGroup(
|
||||
const AZ::Vector2& screenPosition,
|
||||
float distance,
|
||||
const AZStd::string& collisionGroup,
|
||||
AZ::EntityId ignore)
|
||||
{
|
||||
AZ::EntityId camera;
|
||||
Camera::CameraSystemRequestBus::BroadcastResult(camera, &Camera::CameraSystemRequestBus::Events::GetActiveCamera);
|
||||
if (camera.IsValid())
|
||||
{
|
||||
AZ::Vector3 origin = AZ::Vector3::CreateZero();
|
||||
Camera::CameraRequestBus::EventResult(origin, camera, &Camera::CameraRequestBus::Events::ScreenToWorld, screenPosition, 0.f);
|
||||
AZ::Vector3 offset = AZ::Vector3::CreateZero();
|
||||
Camera::CameraRequestBus::EventResult(offset, camera, &Camera::CameraRequestBus::Events::ScreenToWorld, screenPosition, 1.f);
|
||||
const AZ::Vector3 direction = (offset - origin).GetNormalized();
|
||||
return RayCastWorldSpaceWithGroup(origin, direction, distance, collisionGroup, ignore);
|
||||
}
|
||||
|
||||
// fallback in the rare case there is no active camera
|
||||
return AZStd::make_tuple(false, AZ::Vector3::CreateZero(), AZ::Vector3::CreateZero(), 0.0f, AZ::EntityId(), AZ::Crc32());
|
||||
}
|
||||
|
||||
SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(RayCastFromScreenWithGroup,
|
||||
k_categoryName,
|
||||
"{8F98A766-A93F-4DA7-B281-482C3DB20649}",
|
||||
"Returns the first entity hit by a ray cast from the provided absolute 2D screen position.",
|
||||
"Screen Position",
|
||||
"Distance",
|
||||
"Collision group",
|
||||
"Ignore",
|
||||
"Object hit",
|
||||
"Position",
|
||||
"Normal",
|
||||
"Distance",
|
||||
"EntityId",
|
||||
"Surface");
|
||||
|
||||
AZ_INLINE Result RayCastLocalSpaceWithGroup(const AZ::EntityId& fromEntityId,
|
||||
const AZ::Vector3& direction,
|
||||
float distance,
|
||||
@@ -389,6 +427,7 @@ namespace ScriptCanvasPhysics
|
||||
<RayCastWorldSpaceWithGroupNode,
|
||||
RayCastLocalSpaceWithGroupNode,
|
||||
RayCastMultipleLocalSpaceWithGroupNode,
|
||||
RayCastFromScreenWithGroupNode,
|
||||
OverlapSphereWithGroupNode,
|
||||
OverlapBoxWithGroupNode,
|
||||
OverlapCapsuleWithGroupNode,
|
||||
|
||||
Reference in New Issue
Block a user