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/EMotionFX/Code/Source/Editor/PropertyWidgets/ActorJointHandler.cpp

353 lines
14 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Editor/PropertyWidgets/ActorJointHandler.h>
#include <Editor/ActorEditorBus.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <Editor/AnimGraphEditorBus.h>
#include <Editor/JointSelectionDialog.h>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QPushButton>
namespace EMotionFX
{
AZ_CLASS_ALLOCATOR_IMPL(ActorJointPicker, EditorAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL(ActorSingleJointHandler, EditorAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL(ActorMultiJointHandler, EditorAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL(ActorMultiWeightedJointHandler, EditorAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(ActorJointElementHandler, EditorAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(ActorWeightedJointElementHandler, EditorAllocator, 0)
ActorJointPicker::ActorJointPicker(bool singleSelection, const QString& dialogTitle, const QString& dialogDescriptionLabelText, QWidget* parent)
: QWidget(parent)
, m_dialogTitle(dialogTitle)
, m_dialogDescriptionLabelText(dialogDescriptionLabelText)
, m_label(new QLabel())
, m_pickButton(new QPushButton(QIcon(":/SceneUI/Manifest/TreeIcon.png"), ""))
, m_resetButton(new QPushButton())
, m_singleSelection(singleSelection)
{
connect(m_pickButton, &QPushButton::clicked, this, &ActorJointPicker::OnPickClicked);
EMStudio::EMStudioManager::MakeTransparentButton(m_resetButton, "Images/Icons/Clear.svg", "Reset selection");
connect(m_resetButton, &QPushButton::clicked, this, &ActorJointPicker::OnResetClicked);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
hLayout->addWidget(m_label);
hLayout->addStretch();
hLayout->addWidget(m_pickButton);
hLayout->addWidget(m_resetButton);
setLayout(hLayout);
}
void ActorJointPicker::AddDefaultFilter(const QString& category, const QString& displayName)
{
m_defaultFilters.emplace_back(category, displayName);
}
void ActorJointPicker::OnResetClicked()
{
SetWeightedJointNames(AZStd::vector<AZStd::pair<AZStd::string, float> >());
emit SelectionChanged();
}
void ActorJointPicker::SetJointName(const AZStd::string& jointName)
{
AZStd::vector<AZStd::pair<AZStd::string, float> > weightedJointNames;
if (!jointName.empty())
{
weightedJointNames.emplace_back(jointName, 0.0f);
}
SetWeightedJointNames(weightedJointNames);
}
AZStd::string ActorJointPicker::GetJointName() const
{
if (m_weightedJointNames.empty())
{
return AZStd::string();
}
return m_weightedJointNames[0].first;
}
void ActorJointPicker::SetJointNames(const AZStd::vector<AZStd::string>& jointNames)
{
AZStd::vector<AZStd::pair<AZStd::string, float> > weightedJointNames;
const size_t numJointNames = jointNames.size();
weightedJointNames.resize(numJointNames);
for (size_t i = 0; i < numJointNames; ++i)
{
weightedJointNames[i] = AZStd::make_pair<AZStd::string, float>(jointNames[i], 0.0f);
}
SetWeightedJointNames(weightedJointNames);
}
AZStd::vector<AZStd::string> ActorJointPicker::GetJointNames() const
{
AZStd::vector<AZStd::string> result;
const size_t numJoints = m_weightedJointNames.size();
result.resize(numJoints);
for (size_t i = 0; i < numJoints; ++i)
{
result[i] = m_weightedJointNames[i].first;
}
return result;
}
void ActorJointPicker::UpdateInterface()
{
const size_t numJoints = m_weightedJointNames.size();
if (numJoints == 1)
{
m_label->setText(QString::fromUtf8(m_weightedJointNames[0].first.c_str()));
}
else
{
m_label->setText(QString("%1 joint%2 selected").arg(QString::number(numJoints), numJoints == 1 ? QString() : "s"));
}
m_resetButton->setVisible(numJoints > 0);
// Build and set the tooltip containing all joints.
QString tooltip;
for (size_t i = 0; i < numJoints; ++i)
{
tooltip += m_weightedJointNames[i].first.c_str();
if (i != numJoints - 1)
{
tooltip += "\n";
}
}
m_label->setToolTip(tooltip);
}
void ActorJointPicker::SetWeightedJointNames(const AZStd::vector<AZStd::pair<AZStd::string, float> >& weightedJointNames)
{
m_weightedJointNames = weightedJointNames;
UpdateInterface();
}
AZStd::vector<AZStd::pair<AZStd::string, float> > ActorJointPicker::GetWeightedJointNames() const
{
return m_weightedJointNames;
}
void ActorJointPicker::OnPickClicked()
{
EMotionFX::ActorInstance* actorInstance = nullptr;
ActorEditorRequestBus::BroadcastResult(actorInstance, &ActorEditorRequests::GetSelectedActorInstance);
if (!actorInstance)
{
QMessageBox::warning(this, "No Actor Instance", "Cannot open joint selection window. No valid actor instance selected.");
return;
}
// Create and show the joint picker window
JointSelectionDialog jointSelectionWindow(m_singleSelection, m_dialogTitle, m_dialogDescriptionLabelText, this);
for (const auto& filterPair : m_defaultFilters)
{
jointSelectionWindow.SetFilterState(filterPair.first, filterPair.second, true);
}
jointSelectionWindow.HideIcons();
jointSelectionWindow.SelectByJointNames(GetJointNames());
jointSelectionWindow.setModal(true);
if (jointSelectionWindow.exec() != QDialog::Rejected)
{
SetJointNames(jointSelectionWindow.GetSelectedJointNames());
emit SelectionChanged();
}
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
template<class T>
AZ::u32 ActorJointElementHandlerImpl<T>::GetHandlerName() const
{
return AZ_CRC("ActorJointElement", 0xedc8946c);
}
template<>
AZ::u32 ActorJointElementHandlerImpl<AZStd::pair<AZStd::string, float>>::GetHandlerName() const
{
return AZ_CRC("ActorWeightedJointElement", 0xe84566a0);
}
template<class T>
QWidget* ActorJointElementHandlerImpl<T>::CreateGUI(QWidget* parent)
{
AZ_UNUSED(parent);
return nullptr;
}
template<class T>
void ActorJointElementHandlerImpl<T>::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, [[maybe_unused]] QWidget* GUI, [[maybe_unused]] T& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
}
template<class T>
bool ActorJointElementHandlerImpl<T>::ReadValuesIntoGUI([[maybe_unused]] size_t index, [[maybe_unused]] QWidget* GUI, [[maybe_unused]] const T& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
return true;
}
template class ActorJointElementHandlerImpl<AZStd::string>;
template class ActorJointElementHandlerImpl<AZStd::pair<AZStd::string, float>>;
//---------------------------------------------------------------------------------------------------------------------------------------------------------
AZ::u32 ActorSingleJointHandler::GetHandlerName() const
{
return AZ_CRC("ActorNode", 0x35d9eb50);
}
QWidget* ActorSingleJointHandler::CreateGUI(QWidget* parent)
{
ActorJointPicker* picker = aznew ActorJointPicker(true /*singleSelection*/, "Select Joint", "Select or double-click a joint", parent);
connect(picker, &ActorJointPicker::SelectionChanged, this, [picker]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, picker);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, picker);
});
return picker;
}
void ActorSingleJointHandler::ConsumeAttribute(ActorJointPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == AZ::Edit::Attributes::ReadOnly)
{
bool value;
if (attrValue->Read<bool>(value))
{
GUI->setEnabled(!value);
}
}
}
void ActorSingleJointHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, ActorJointPicker* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetJointName();
}
bool ActorSingleJointHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, ActorJointPicker* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
QSignalBlocker signalBlocker(GUI);
GUI->SetJointName(instance);
return true;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
AZ::u32 ActorMultiJointHandler::GetHandlerName() const
{
return AZ_CRC("ActorNodes", 0x70504714);
}
QWidget* ActorMultiJointHandler::CreateGUI(QWidget* parent)
{
ActorJointPicker* picker = aznew ActorJointPicker(false /*singleSelection*/, "Select Joints", "Select one or more joints from the skeleton", parent);
connect(picker, &ActorJointPicker::SelectionChanged, this, [picker]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, picker);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, picker);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::Refresh_EntireTree);
});
return picker;
}
void ActorMultiJointHandler::ConsumeAttribute(ActorJointPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == AZ::Edit::Attributes::ReadOnly)
{
bool value;
if (attrValue->Read<bool>(value))
{
GUI->setEnabled(!value);
}
}
}
void ActorMultiJointHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, ActorJointPicker* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetJointNames();
}
bool ActorMultiJointHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, ActorJointPicker* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
QSignalBlocker signalBlocker(GUI);
GUI->SetJointNames(instance);
return true;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
AZ::u32 ActorMultiWeightedJointHandler::GetHandlerName() const
{
return AZ_CRC("ActorWeightedNodes", 0x689c0537);
}
QWidget* ActorMultiWeightedJointHandler::CreateGUI(QWidget* parent)
{
ActorJointPicker* picker = aznew ActorJointPicker(false /*singleSelection*/, "Joint Selection Dialog", "Select one or more joints from the skeleton", parent);
connect(picker, &ActorJointPicker::SelectionChanged, this, [picker]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, picker);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, picker);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::Refresh_EntireTree);
});
return picker;
}
void ActorMultiWeightedJointHandler::ConsumeAttribute(ActorJointPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == AZ::Edit::Attributes::ReadOnly)
{
bool value;
if (attrValue->Read<bool>(value))
{
GUI->setEnabled(!value);
}
}
}
void ActorMultiWeightedJointHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, ActorJointPicker* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetWeightedJointNames();
}
bool ActorMultiWeightedJointHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, ActorJointPicker* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
QSignalBlocker signalBlocker(GUI);
GUI->SetWeightedJointNames(instance);
return true;
}
} // namespace EMotionFX