You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxPolygonTranslationM...

316 lines
14 KiB
C++

/*
* 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 "EditorWhiteBoxComponentModeBus.h"
#include "EditorWhiteBoxPolygonModifierBus.h"
#include "SubComponentModes/EditorWhiteBoxDefaultModeBus.h"
#include "Viewport/WhiteBoxManipulatorViews.h"
#include "Viewport/WhiteBoxModifierUtil.h"
#include "Viewport/WhiteBoxViewportConstants.h"
#include "WhiteBoxPolygonTranslationModifier.h"
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <WhiteBox/EditorWhiteBoxComponentBus.h>
namespace WhiteBox
{
AZ_CLASS_ALLOCATOR_IMPL(PolygonTranslationModifier, AZ::SystemAllocator, 0)
PolygonTranslationModifier::PolygonTranslationModifier(
const AZ::EntityComponentIdPair& entityComponentIdPair, const Api::PolygonHandle& polygonHandle,
[[maybe_unused]] const AZ::Vector3& intersectionPoint)
: m_entityComponentIdPair(entityComponentIdPair)
, m_polygonHandle(polygonHandle)
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
m_vertexHandles = Api::PolygonVertexHandles(*whiteBox, m_polygonHandle);
CreateManipulator();
}
PolygonTranslationModifier::~PolygonTranslationModifier()
{
DestroyManipulator();
}
void PolygonTranslationModifier::CreateManipulator()
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
m_translationManipulator = AzToolsFramework::LinearManipulator::MakeShared(
AzToolsFramework::WorldFromLocalWithUniformScale(m_entityComponentIdPair.GetEntityId()));
m_translationManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
m_translationManipulator->SetLocalPosition(Api::PolygonMidpoint(*whiteBox, m_polygonHandle));
m_translationManipulator->SetAxis(Api::PolygonNormal(*whiteBox, m_polygonHandle));
CreateView();
m_translationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
struct SharedState
{
AZStd::vector<AZ::Vector3> m_vertexPositions;
// what state of appending are we currently in
AppendStage m_appendStage = AppendStage::None;
// the position of the manipulator the moment an append is initiated
AZ::Vector3 m_initiateAppendPosition = AZ::Vector3::CreateZero();
// the distance the manipulator has moved from where it started when an append begins
AZ::Vector3 m_activeAppendOffset = AZ::Vector3::CreateZero();
// the midpoint of the edge manipulator
AZ::Vector3 m_polygonMidpoint = AZ::Vector3::CreateZero();
// has the modifier moved during the action
bool m_moved = false;
};
auto sharedState = AZStd::make_shared<SharedState>();
m_translationManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/)
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
sharedState->m_appendStage = AppendStage::None;
sharedState->m_activeAppendOffset = AZ::Vector3::CreateZero();
sharedState->m_vertexPositions = Api::VertexPositions(*whiteBox, m_vertexHandles);
sharedState->m_polygonMidpoint = Api::PolygonMidpoint(*whiteBox, m_polygonHandle);
sharedState->m_moved = false;
});
m_translationManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) mutable
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
// has the modifier moved during this interaction
sharedState->m_moved = sharedState->m_moved ||
action.LocalPositionOffset().GetLength() >= cl_whiteBoxMouseClickDeltaThreshold;
// reset append
if (!action.m_modifiers.Ctrl() && sharedState->m_appendStage != AppendStage::None)
{
sharedState->m_appendStage = AppendStage::None;
}
// start trying to extrude
if (action.m_modifiers.Ctrl() && sharedState->m_appendStage == AppendStage::None)
{
sharedState->m_appendStage = AppendStage::Initiated;
sharedState->m_initiateAppendPosition = action.LocalPosition();
}
if (sharedState->m_appendStage == AppendStage::Initiated)
{
const AZ::Vector3 extrudeVector = action.LocalPosition() - sharedState->m_initiateAppendPosition;
const float extrudeMagnitude = extrudeVector.Dot(action.m_fixed.m_axis);
// only extrude after having moved a small amount (to prevent overlapping verts
// and normals being calculated incorrectly)
if (fabsf(extrudeMagnitude) > 0.0f)
{
// extrude the new side
const auto appendedPolygonHandles =
Api::TranslatePolygonAppendAdvanced(*whiteBox, m_polygonHandle, extrudeMagnitude);
// update our shared state to hold the new values after extrusion
m_vertexHandles =
Api::PolygonVertexHandles(*whiteBox, appendedPolygonHandles.m_appendedPolygonHandle);
sharedState->m_appendStage = AppendStage::Complete;
// remember the current offset when we start extruding (to stop any snapping)
sharedState->m_activeAppendOffset = action.LocalPositionOffset();
sharedState->m_polygonMidpoint =
Api::PolygonMidpoint(*whiteBox, appendedPolygonHandles.m_appendedPolygonHandle);
// make sure all vertex positions are refreshed and match the correct handle
for (size_t vertexIndex = 0; vertexIndex < m_vertexHandles.size(); ++vertexIndex)
{
const Api::VertexHandle vertexHandle = m_vertexHandles[vertexIndex];
sharedState->m_vertexPositions[vertexIndex] = Api::VertexPosition(*whiteBox, vertexHandle);
}
// notify primary polygon modifier has changed
EditorWhiteBoxPolygonModifierNotificationBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxPolygonModifierNotificationBus::Events::
OnPolygonModifierUpdatedPolygonHandle,
m_polygonHandle, appendedPolygonHandles.m_appendedPolygonHandle);
// notify all other restored polygon handle pairs (that may have been removed and added)
for (const auto& restoredPolygonHandlePair : appendedPolygonHandles.m_restoredPolygonHandles)
{
EditorWhiteBoxPolygonModifierNotificationBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxPolygonModifierNotificationBus::Events::
OnPolygonModifierUpdatedPolygonHandle,
restoredPolygonHandlePair.m_before, restoredPolygonHandlePair.m_after);
}
m_polygonHandle = appendedPolygonHandles.m_appendedPolygonHandle;
}
}
// regular movement/translation of vertices
if (sharedState->m_appendStage == AppendStage::None ||
sharedState->m_appendStage == AppendStage::Complete)
{
size_t vertexIndex = 0;
for (const Api::VertexHandle vertexHandle : m_vertexHandles)
{
const AZ::Vector3 vertexPosition = sharedState->m_vertexPositions[vertexIndex++] +
action.LocalPositionOffset() - sharedState->m_activeAppendOffset;
Api::SetVertexPosition(*whiteBox, vertexHandle, vertexPosition);
}
m_translationManipulator->SetLocalPosition(
sharedState->m_polygonMidpoint + action.LocalPositionOffset() -
sharedState->m_activeAppendOffset);
EditorWhiteBoxComponentModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxComponentModeRequestBus::Events::MarkWhiteBoxIntersectionDataDirty);
EditorWhiteBoxDefaultModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::RefreshPolygonScaleModifier);
EditorWhiteBoxDefaultModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::RefreshEdgeScaleModifier);
EditorWhiteBoxDefaultModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::RefreshPolygonTranslationModifier);
EditorWhiteBoxDefaultModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::RefreshEdgeTranslationModifier);
EditorWhiteBoxDefaultModeRequestBus::Event(
m_entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::RefreshVertexSelectionModifier);
}
Api::CalculateNormals(*whiteBox);
Api::CalculatePlanarUVs(*whiteBox);
// inefficient but easy/effective - just rebuild the whole mesh after every change
EditorWhiteBoxComponentNotificationBus::Event(
m_entityComponentIdPair, &EditorWhiteBoxComponentNotificationBus::Events::OnWhiteBoxMeshModified);
});
m_translationManipulator->InstallLeftMouseUpCallback(
[entityComponentIdPair = m_entityComponentIdPair, sharedState,
this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
{
// we haven't moved, count as a click
if (!sharedState->m_moved)
{
EditorWhiteBoxDefaultModeRequestBus::Event(
entityComponentIdPair, &EditorWhiteBoxDefaultModeRequestBus::Events::CreatePolygonScaleModifier,
m_polygonHandle);
EditorWhiteBoxDefaultModeRequestBus::Event(
entityComponentIdPair,
&EditorWhiteBoxDefaultModeRequestBus::Events::AssignSelectedPolygonTranslationModifier);
}
else
{
EditorWhiteBoxComponentRequestBus::Event(
entityComponentIdPair, &EditorWhiteBoxComponentRequests::SerializeWhiteBox);
}
});
}
void PolygonTranslationModifier::DestroyManipulator()
{
m_translationManipulator->Unregister();
m_translationManipulator.reset();
}
void PolygonTranslationModifier::ForwardMouseOverEvent(
const AzToolsFramework::ViewportInteraction::MouseInteraction& interaction)
{
m_translationManipulator->ForwardMouseOverEvent(interaction);
}
bool PolygonTranslationModifier::MouseOver() const
{
return m_translationManipulator->MouseOver();
}
void PolygonTranslationModifier::Refresh()
{
DestroyManipulator();
CreateManipulator();
}
void PolygonTranslationModifier::SetPolygonHandle(const Api::PolygonHandle& polygonHandle)
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
m_polygonHandle = polygonHandle;
// ensure we update our cached values
m_vertexHandles = Api::PolygonVertexHandles(*whiteBox, polygonHandle);
}
void PolygonTranslationModifier::CreateView()
{
WhiteBoxMesh* whiteBox = nullptr;
EditorWhiteBoxComponentRequestBus::EventResult(
whiteBox, m_entityComponentIdPair, &EditorWhiteBoxComponentRequests::GetWhiteBoxMesh);
Api::VertexPositionsCollection outlines = Api::PolygonBorderVertexPositions(*whiteBox, m_polygonHandle);
AZStd::vector<AZ::Vector3> triangles = Api::PolygonFacesPositions(*whiteBox, m_polygonHandle);
const AZ::Vector3 polygonMidpoint = Api::PolygonMidpoint(*whiteBox, m_polygonHandle);
// translate points into local space of the manipulator (see UpdateIntersectionPoint)
// (relative to m_translationManipulator local position)
for (auto& outline : outlines)
{
TranslatePoints(outline, -polygonMidpoint);
}
TranslatePoints(triangles, -polygonMidpoint);
if (!m_polygonView)
{
m_polygonView = CreateManipulatorViewPolygon(triangles, outlines);
}
else
{
m_polygonView->m_outlines = outlines;
m_polygonView->m_triangles = triangles;
}
m_polygonView->m_fillColor = m_fillColor;
m_polygonView->m_outlineColor = m_outlineColor;
m_translationManipulator->SetViews(AzToolsFramework::ManipulatorViews{m_polygonView});
}
bool PolygonTranslationModifier::PerformingAction() const
{
return m_translationManipulator->PerformingAction();
}
} // namespace WhiteBox