Support mesh intersection for camera orbit (#982)

* wip support for mesh intersection with intersector bus

* WIP camera mesh intersection orbit logic

* remove unneeded template argument

* add bus connect/disconnect

* fix intersection logic

* small updates, additional comments, some tidy-up

* update formatting options slightly

* use aznumeric_cast

* temp workaround for negative distances with RayIntersection
This commit is contained in:
Tom Hulton-Harrop
2021-05-28 14:37:17 +01:00
committed by GitHub
parent c9f7cd03bb
commit 36ceff84c9
9 changed files with 191 additions and 100 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ SortIncludes: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
@@ -12,6 +12,7 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Render/GeometryIntersectionStructures.h>
namespace AzFramework
@@ -35,12 +36,12 @@ namespace AzFramework
AzFramework::EntityContextId m_contextId;
};
//! Interface for intersection requests, implement this interface for making your component
//! render geometry intersectable.
//! Interface for intersection requests.
//! Implement this interface to make your component 'intersectable'.
class IntersectionRequests
: public AZ::EBusTraits
{
//! Policy for notifying the Intersector bus of entities connected/disconnected to this ebus
//! Policy for notifying the Intersector bus of entities connected/disconnected to this EBus
//! so it updates the internal data of the entities
template<class Bus>
struct IntersectionRequestsConnectionPolicy
@@ -144,7 +144,7 @@ namespace AzFramework
z = AZStd::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
}
return {x, y, z};
return { x, y, z };
}
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform)
@@ -179,7 +179,7 @@ namespace AzFramework
{
const auto nextCamera = m_cameras.StepCamera(targetCamera, m_motionDelta, m_scrollDelta, deltaTime);
m_motionDelta = ScreenVector{0, 0};
m_motionDelta = ScreenVector{ 0, 0 };
m_scrollDelta = 0.0f;
return nextCamera;
@@ -213,7 +213,10 @@ namespace AzFramework
auto& cameraInput = m_idleCameraInputs[i];
const bool canBegin = cameraInput->Beginning() &&
AZStd::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
[](const auto& input) { return !input->Exclusive(); }) &&
[](const auto& input)
{
return !input->Exclusive();
}) &&
(!cameraInput->Exclusive() || (cameraInput->Exclusive() && m_activeCameraInputs.empty()));
if (canBegin)
@@ -231,7 +234,8 @@ namespace AzFramework
const Camera nextCamera = AZStd::accumulate(
AZStd::begin(m_activeCameraInputs), AZStd::end(m_activeCameraInputs), targetCamera,
[cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) {
[cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera)
{
acc = camera->StepCamera(acc, cursorDelta, scrollDelta, deltaTime);
return acc;
});
@@ -284,7 +288,8 @@ namespace AzFramework
bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
const ClickDetector::ClickEvent clickEvent = [&event, this] {
const ClickDetector::ClickEvent clickEvent = [&event, this]
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_rotateChannelId)
@@ -330,7 +335,10 @@ namespace AzFramework
nextCamera.m_pitch -= float(cursorDelta.m_y) * ed_cameraSystemRotateSpeed;
nextCamera.m_yaw -= float(cursorDelta.m_x) * ed_cameraSystemRotateSpeed;
const auto clampRotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
const auto clampRotation = [](const float angle)
{
return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
};
nextCamera.m_yaw = clampRotation(nextCamera.m_yaw);
// clamp pitch to be +-90 degrees
@@ -377,9 +385,10 @@ namespace AzFramework
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * ed_cameraSystemPanSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * ed_cameraSystemPanSpeed;
const auto inv = [](const bool invert) {
constexpr float Dir[] = {1.0f, -1.0f};
return Dir[static_cast<int>(invert)];
const auto inv = [](const bool invert)
{
constexpr float Dir[] = { 1.0f, -1.0f };
return Dir[aznumeric_cast<int>(invert)];
};
nextCamera.m_lookAt += deltaPanX * inv(ed_cameraSystemPanInvertX);
@@ -475,7 +484,8 @@ namespace AzFramework
const auto axisY = translationBasis.GetBasisY();
const auto axisZ = translationBasis.GetBasisZ();
const float speed = [boost = m_boost]() {
const float speed = [boost = m_boost]()
{
return ed_cameraSystemTranslateSpeed * (boost ? ed_cameraSystemBoostMultiplier : 1.0f);
}();
@@ -555,10 +565,12 @@ namespace AzFramework
if (Beginning())
{
const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] {
const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn]
{
if (lookAtFn)
{
if (const auto lookAt = lookAtFn())
// pass through the camera's position and look vector for use in the lookAt function
if (const auto lookAt = lookAtFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()))
{
auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt);
nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt);
@@ -692,14 +704,20 @@ namespace AzFramework
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const float deltaTime)
{
const auto clamp_rotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
const auto clamp_rotation = [](const float angle)
{
return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
};
// keep yaw in 0 - 360 range
float targetYaw = clamp_rotation(targetCamera.m_yaw);
const float currentYaw = clamp_rotation(currentCamera.m_yaw);
// return the sign of the float input (-1, 0, 1)
const auto sign = [](const float value) { return aznumeric_cast<float>((0.0f < value) - (value < 0.0f)); };
const auto sign = [](const float value)
{
return aznumeric_cast<float>((0.0f < value) - (value < 0.0f));
};
// ensure smooth transition when moving across 0 - 360 boundary
const float yawDelta = targetYaw - currentYaw;
@@ -727,26 +745,28 @@ namespace AzFramework
const auto& inputChannelId = inputChannel.GetInputChannelId();
const auto& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
const bool wasMouseButton =
AZStd::any_of(InputDeviceMouse::Button::All.begin(), InputDeviceMouse::Button::All.end(), [inputChannelId](const auto& button) {
const bool wasMouseButton = AZStd::any_of(
InputDeviceMouse::Button::All.begin(), InputDeviceMouse::Button::All.end(),
[inputChannelId](const auto& button)
{
return button == inputChannelId;
});
if (inputChannelId == InputDeviceMouse::Movement::X)
{
return HorizontalMotionEvent{(int)inputChannel.GetValue()};
return HorizontalMotionEvent{ aznumeric_cast<int>(inputChannel.GetValue()) };
}
else if (inputChannelId == InputDeviceMouse::Movement::Y)
{
return VerticalMotionEvent{(int)inputChannel.GetValue()};
return VerticalMotionEvent{ aznumeric_cast<int>(inputChannel.GetValue()) };
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
return ScrollEvent{inputChannel.GetValue()};
return ScrollEvent{ inputChannel.GetValue() };
}
else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{inputChannelId, inputChannel.GetState()};
return DiscreteInputEvent{ inputChannelId, inputChannel.GetState() };
}
return AZStd::monostate{};
@@ -34,9 +34,9 @@ namespace AzFramework
AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero,
//!< or position of m_lookAt when m_lookDist is greater
//!< than zero.
float m_yaw{0.0};
float m_pitch{0.0};
float m_lookDist{0.0}; //!< Zero gives first person free look, otherwise orbit about m_lookAt
float m_yaw{ 0.0 };
float m_pitch{ 0.0 };
float m_lookDist{ 0.0 }; //!< Zero gives first person free look, otherwise orbit about m_lookAt
//! View camera transform (v in MVP).
AZ::Transform View() const;
@@ -195,7 +195,11 @@ namespace AzFramework
inline bool Cameras::Exclusive() const
{
return AZStd::any_of(
m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); });
m_activeCameraInputs.begin(), m_activeCameraInputs.end(),
[](const auto& cameraInput)
{
return cameraInput->Exclusive();
});
}
//! Responsible for updating a series of cameras given various inputs.
@@ -209,7 +213,7 @@ namespace AzFramework
private:
ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional.
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
};
class RotateCameraInput : public CameraInput
@@ -237,7 +241,7 @@ namespace AzFramework
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return {orientation.GetBasisX(), orientation.GetBasisZ()};
return { orientation.GetBasisX(), orientation.GetBasisZ() };
}
inline PanAxes OrbitPan(const Camera& camera)
@@ -245,12 +249,13 @@ namespace AzFramework
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto basisY = [&orientation]
{
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
return {basisX, basisY};
return { basisX, basisY };
}
class PanCameraInput : public CameraInput
@@ -285,7 +290,8 @@ namespace AzFramework
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto basisY = [&orientation]
{
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
@@ -398,7 +404,7 @@ namespace AzFramework
class OrbitCameraInput : public CameraInput
{
public:
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>(const AZ::Vector3& position, const AZ::Vector3& direction)>;
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
+62 -39
View File
@@ -1221,50 +1221,73 @@ void EditorViewportWidget::SetViewportId(int id)
AzFramework::ReloadCameraKeyBindings();
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
controller->SetCameraListBuilderCallback([](AzFramework::Cameras& cameras)
{
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
auto firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraFreePanButton, AzFramework::LookPan);
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
controller->SetCameraListBuilderCallback(
[](AzFramework::Cameras& cameras)
{
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
auto firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraFreePanButton, AzFramework::LookPan);
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
orbitCamera->SetLookAtFn([]() -> AZStd::optional<AZ::Vector3> {
AZStd::optional<AZ::Transform> manipulatorTransform;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
orbitCamera->SetLookAtFn(
[](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
{
AZStd::optional<AZ::Transform> manipulatorTransform;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
if (manipulatorTransform)
{
return manipulatorTransform->GetTranslation();
}
// initially attempt to use manipulator transform if one exists (there is a selection)
if (manipulatorTransform)
{
return manipulatorTransform->GetTranslation();
}
return {};
const float RayDistance = 1000.0f;
AzFramework::RenderGeometry::RayRequest ray;
ray.m_startWorldPosition = position;
ray.m_endWorldPosition = position + direction * RayDistance;
ray.m_onlyVisible = true;
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
AzFramework::RenderGeometry::IntersectorBus::EventResult(
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
&AzFramework::RenderGeometry::IntersectorInterface::RayIntersect, ray);
// attempt a ray intersection with any visible mesh and return the intersection position if successful
if (renderGeometryIntersectionResult)
{
return renderGeometryIntersectionResult.m_worldPosition;
}
// if there is no selection or no intersection, fallback to default camera orbit behavior (ground plane
// intersection)
return {};
});
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraOrbitLookButton);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
auto orbitDollyMoveCamera =
AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>(AzFramework::CameraOrbitDollyButton);
auto orbitPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera);
cameras.AddCamera(firstPersonRotateCamera);
cameras.AddCamera(firstPersonPanCamera);
cameras.AddCamera(firstPersonTranslateCamera);
cameras.AddCamera(firstPersonWheelCamera);
cameras.AddCamera(orbitCamera);
});
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraOrbitLookButton);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
auto orbitDollyMoveCamera =
AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>(AzFramework::CameraOrbitDollyButton);
auto orbitPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera);
cameras.AddCamera(firstPersonRotateCamera);
cameras.AddCamera(firstPersonPanCamera);
cameras.AddCamera(firstPersonTranslateCamera);
cameras.AddCamera(firstPersonWheelCamera);
cameras.AddCamera(orbitCamera);
});
m_renderViewport->GetControllerList()->Add(controller);
}
else
@@ -35,14 +35,13 @@ namespace AZ
, private MeshComponentNotificationBus::Handler
{
public:
using BaseClass = EditorRenderComponentAdapter<MeshComponentController, MeshComponent, MeshComponentConfig>;
AZ_EDITOR_COMPONENT(AZ::Render::EditorMeshComponent, EditorMeshComponentTypeId, BaseClass);
static void Reflect(AZ::ReflectContext* context);
EditorMeshComponent() = default;
EditorMeshComponent(const MeshComponentConfig& config);
explicit EditorMeshComponent(const MeshComponentConfig& config);
// AZ::Component overrides ...
void Activate() override;
@@ -25,12 +25,11 @@ namespace AZ
: public AzFramework::Components::ComponentAdapter<MeshComponentController, MeshComponentConfig>
{
public:
using BaseClass = AzFramework::Components::ComponentAdapter<MeshComponentController, MeshComponentConfig>;
AZ_COMPONENT(AZ::Render::MeshComponent, MeshComponentTypeId, BaseClass);
MeshComponent() = default;
MeshComponent(const MeshComponentConfig& config);
explicit MeshComponent(const MeshComponentConfig& config);
static void Reflect(AZ::ReflectContext* context);
};
@@ -177,28 +177,33 @@ namespace AZ
FixUpModelAsset(m_configuration.m_modelAsset);
}
void MeshComponentController::Activate(AZ::EntityId entityId)
void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair)
{
FixUpModelAsset(m_configuration.m_modelAsset);
m_entityId = entityId;
const AZ::EntityId entityId = entityComponentIdPair.GetEntityId();
m_entityComponentIdPair = entityComponentIdPair;
m_transformInterface = TransformBus::FindFirstHandler(m_entityId);
m_transformInterface = TransformBus::FindFirstHandler(entityId);
AZ_Warning("MeshComponentController", m_transformInterface, "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin.");
m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(m_entityId);
m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(entityId);
AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
m_cachedNonUniformScale = AZ::Vector3::CreateOne();
AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale);
AZ::NonUniformScaleRequestBus::Event(m_entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale);
AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
m_nonUniformScaleChangedHandler);
MeshComponentRequestBus::Handler::BusConnect(m_entityId);
TransformNotificationBus::Handler::BusConnect(m_entityId);
MaterialReceiverRequestBus::Handler::BusConnect(m_entityId);
MaterialComponentNotificationBus::Handler::BusConnect(m_entityId);
AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
MeshComponentRequestBus::Handler::BusConnect(entityId);
TransformNotificationBus::Handler::BusConnect(entityId);
MaterialReceiverRequestBus::Handler::BusConnect(entityId);
MaterialComponentNotificationBus::Handler::BusConnect(entityId);
AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
AzFramework::EntityContextId contextId;
AzFramework::EntityIdContextQueryBus::EventResult(
contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId});
//Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange
RegisterModel();
@@ -209,6 +214,7 @@ namespace AZ
// Buses must be disconnected after unregistering the model, otherwise they can't deliver the events during the process.
UnregisterModel();
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusDisconnect();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
MeshComponentRequestBus::Handler::BusDisconnect();
TransformNotificationBus::Handler::BusDisconnect();
@@ -219,7 +225,7 @@ namespace AZ
m_meshFeatureProcessor = nullptr;
m_transformInterface = nullptr;
m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
m_entityComponentIdPair = AZ::EntityComponentIdPair(AZ::EntityId(), AZ::InvalidComponentId);
m_configuration.m_modelAsset.Release();
}
@@ -293,10 +299,11 @@ namespace AZ
Data::Asset<RPI::ModelAsset> modelAsset = m_meshFeatureProcessor->GetModelAsset(m_meshHandle);
if (model && modelAsset)
{
const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
m_configuration.m_modelAsset = modelAsset;
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model);
MaterialReceiverNotificationBus::Event(m_entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged);
AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(m_entityId);
MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model);
MaterialReceiverNotificationBus::Event(entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged);
AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(entityId);
}
}
@@ -304,8 +311,10 @@ namespace AZ
{
if (m_meshFeatureProcessor && m_configuration.m_modelAsset.GetId().IsValid())
{
const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
MaterialAssignmentMap materials;
MaterialComponentRequestBus::EventResult(materials, m_entityId, &MaterialComponentRequests::GetMaterialOverrides);
MaterialComponentRequestBus::EventResult(materials, entityId, &MaterialComponentRequests::GetMaterialOverrides);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_configuration.m_modelAsset, materials,
@@ -330,7 +339,8 @@ namespace AZ
{
if (m_meshFeatureProcessor && m_meshHandle.IsValid())
{
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelPreDestroy);
MeshComponentNotificationBus::Event(
m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnModelPreDestroy);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
}
}
@@ -462,5 +472,34 @@ namespace AZ
return Aabb::CreateNull();
}
}
AzFramework::RenderGeometry::RayResult MeshComponentController::RenderGeometryIntersect(
const AzFramework::RenderGeometry::RayRequest& ray)
{
AzFramework::RenderGeometry::RayResult result;
if (const Data::Instance<RPI::Model> model = GetModel())
{
float t;
AZ::Vector3 normal;
if (model->RayIntersection(
m_transformInterface->GetWorldTM(), m_cachedNonUniformScale, ray.m_startWorldPosition,
ray.m_endWorldPosition - ray.m_startWorldPosition, t, normal))
{
// note: this is a temporary workaround to handle cases where model->RayIntersection
// returns negative distances, follow-up ATOM-15673
const auto absT = AZStd::abs(t);
// fill in ray result structure after successful intersection
const auto intersectionLine = (ray.m_endWorldPosition - ray.m_startWorldPosition);
result.m_uv = AZ::Vector2::CreateZero();
result.m_worldPosition = ray.m_startWorldPosition + intersectionLine * absT;
result.m_worldNormal = normal;
result.m_distance = intersectionLine.GetLength() * absT;
result.m_entityAndComponent = m_entityComponentIdPair;
}
}
return result;
}
} // namespace Render
} // namespace AZ
@@ -13,11 +13,13 @@
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/NonUniformScaleBus.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzFramework/Render/GeometryIntersectionBus.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <Atom/RPI.Public/Model/Model.h>
@@ -32,9 +34,7 @@ namespace AZ
{
namespace Render
{
/**
* A configuration structure for the MeshComponentController
*/
//! A configuration structure for the MeshComponentController
class MeshComponentConfig final
: public AZ::ComponentConfig
{
@@ -57,6 +57,7 @@ namespace AZ
class MeshComponentController final
: private MeshComponentRequestBus::Handler
, public AzFramework::BoundsRequestBus::Handler
, public AzFramework::RenderGeometry::IntersectionRequestBus::Handler
, private TransformNotificationBus::Handler
, private MaterialReceiverRequestBus::Handler
, private MaterialComponentNotificationBus::Handler
@@ -77,7 +78,7 @@ namespace AZ
MeshComponentController() = default;
MeshComponentController(const MeshComponentConfig& config);
void Activate(AZ::EntityId entityId);
void Activate(const AZ::EntityComponentIdPair& entityComponentIdPair);
void Deactivate();
void SetConfiguration(const MeshComponentConfig& config);
const MeshComponentConfig& GetConfiguration() const;
@@ -103,10 +104,13 @@ namespace AZ
void SetVisibility(bool visible) override;
bool GetVisibility() const override;
// BoundsRequestBus and MeshComponentRequestBus ...
// BoundsRequestBus and MeshComponentRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
// IntersectionRequestBus overrides ...
AzFramework::RenderGeometry::RayResult RenderGeometryIntersect(const AzFramework::RenderGeometry::RayRequest& ray) override;
// TransformNotificationBus::Handler overrides ...
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
@@ -134,7 +138,7 @@ namespace AZ
Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
TransformInterface* m_transformInterface = nullptr;
AZ::EntityId m_entityId;
AZ::EntityComponentIdPair m_entityComponentIdPair;
bool m_isVisible = true;
MeshComponentConfig m_configuration;
AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne();