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:
committed by
GitHub
parent
c9f7cd03bb
commit
36ceff84c9
+1
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
+56
-17
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user