ScreenToWorld and WorldToScreen Camera functionality (#6903)

Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com>
This commit is contained in:
Alex Peterson
2022-01-14 16:43:01 -08:00
committed by GitHub
parent 5885270640
commit b87e6896e6
9 changed files with 389 additions and 2 deletions
@@ -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>;
@@ -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 @@
]
}
]
}
}
@@ -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,