Files
o3de/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp
T
Steve Pham 38261d0800 Shorten copyright headers by splitting into 2 lines (#2213)
* Updated all copyright headers to split the longer original copyright line into 2 shorter lines

Signed-off-by: Steve Pham <spham@amazon.com>
2021-07-16 15:25:48 -07:00

254 lines
9.8 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 <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/Ragdoll.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/Node.h>
#include <EMotionFX/CommandSystem/Source/ColliderCommands.h>
#include <Editor/ColliderContainerWidget.h>
#include <Editor/ColliderHelpers.h>
#include <Editor/ObjectEditor.h>
#include <Editor/SkeletonModel.h>
#include <Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h>
#include <Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h>
#include <Editor/Plugins/Ragdoll/RagdollNodeWidget.h>
#include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerBus.h>
#include <QLabel>
#include <QMessageBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
namespace EMotionFX
{
RagdollCardHeader::RagdollCardHeader(QWidget* parent)
: AzQtComponents::CardHeader(parent)
{
m_backgroundFrame->setObjectName("");
}
RagdollCard::RagdollCard(QWidget* parent)
: AzQtComponents::Card(new RagdollCardHeader(), parent)
{
hideFrame();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RagdollNodeWidget::RagdollNodeWidget(QWidget* parent)
: SkeletonModelJointWidget(parent)
, m_ragdollNodeCard(nullptr)
, m_ragdollNodeEditor(nullptr)
, m_addRemoveButton(nullptr)
, m_jointLimitWidget(nullptr)
, m_addColliderButton(nullptr)
, m_collidersWidget(nullptr)
{
}
QWidget* RagdollNodeWidget::CreateContentWidget(QWidget* parent)
{
QWidget* result = new QWidget(parent);
QVBoxLayout* layout = new QVBoxLayout();
layout->setMargin(0);
layout->setSpacing(ColliderContainerWidget::s_layoutSpacing);
result->setLayout(layout);
// Ragdoll node widget
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Error("EMotionFX", serializeContext, "Can't get serialize context from component application.");
m_ragdollNodeEditor = new EMotionFX::ObjectEditor(serializeContext, result);
m_ragdollNodeCard = new RagdollCard(result);
m_ragdollNodeCard->setTitle("Ragdoll properties");
m_ragdollNodeCard->setContentWidget(m_ragdollNodeEditor);
m_ragdollNodeCard->setExpanded(true);
AzQtComponents::CardHeader* cardHeader = m_ragdollNodeCard->header();
cardHeader->setHasContextMenu(false);
layout->addWidget(m_ragdollNodeCard);
// Buttons
QVBoxLayout* buttonLayout = new QVBoxLayout();
layout->addLayout(buttonLayout);
m_addColliderButton = new AddColliderButton("Add ragdoll collider", result, PhysicsSetup::ColliderConfigType::Ragdoll);
connect(m_addColliderButton, &AddColliderButton::AddCollider, this, &RagdollNodeWidget::OnAddCollider);
buttonLayout->addWidget(m_addColliderButton);
m_addRemoveButton = new QPushButton(result);
m_addRemoveButton->setObjectName("EMFX.RagdollNodeWidget.PushButton.RagdollAddRemoveButton");
connect(m_addRemoveButton, &QPushButton::clicked, this, &RagdollNodeWidget::OnAddRemoveRagdollNode);
buttonLayout->addWidget(m_addRemoveButton);
// Joint limit
m_jointLimitWidget = new RagdollJointLimitWidget(m_copiedJointLimit, result);
connect(m_jointLimitWidget, &RagdollJointLimitWidget::JointLimitCopied, [this](const AZStd::string& serializedJointLimits)
{
m_copiedJointLimit = serializedJointLimits;
});
layout->addWidget(m_jointLimitWidget);
// Colliders
m_collidersWidget = new ColliderContainerWidget(QIcon(SkeletonModel::s_ragdollColliderIconPath), result);
connect(m_collidersWidget, &ColliderContainerWidget::CopyCollider, this, &RagdollNodeWidget::OnCopyCollider);
connect(m_collidersWidget, &ColliderContainerWidget::PasteCollider, this, &RagdollNodeWidget::OnPasteCollider);
connect(m_collidersWidget, &ColliderContainerWidget::RemoveCollider, this, &RagdollNodeWidget::OnRemoveCollider);
layout->addWidget(m_collidersWidget);
return result;
}
QWidget* RagdollNodeWidget::CreateNoSelectionWidget(QWidget* parent)
{
QLabel* noSelectionLabel = new QLabel("Select joints from the Skeleton Outliner and add it to the ragdoll using the right-click menu", parent);
noSelectionLabel->setWordWrap(true);
return noSelectionLabel;
}
void RagdollNodeWidget::InternalReinit()
{
if (m_selectedModelIndices.size() == 1)
{
m_ragdollNodeEditor->ClearInstances(false);
Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = GetRagdollColliderNodeConfig();
Physics::RagdollNodeConfiguration* ragdollNodeConfig = GetRagdollNodeConfig();
if (ragdollNodeConfig)
{
m_addColliderButton->show();
m_addRemoveButton->setText("Remove from ragdoll");
m_ragdollNodeEditor->AddInstance(ragdollNodeConfig, azrtti_typeid(ragdollNodeConfig));
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Error("EMotionFX", serializeContext, "Can't get serialize context from component application.");
if (colliderNodeConfig)
{
m_collidersWidget->Update(GetActor(), GetNode(), PhysicsSetup::ColliderConfigType::Ragdoll, colliderNodeConfig->m_shapes, serializeContext);
}
else
{
m_collidersWidget->Reset();
}
m_jointLimitWidget->Update(m_selectedModelIndices[0]);
m_ragdollNodeCard->setExpanded(true);
m_ragdollNodeCard->show();
m_jointLimitWidget->show();
m_collidersWidget->show();
}
else
{
m_addColliderButton->hide();
m_addRemoveButton->setText("Add to ragdoll");
m_collidersWidget->Reset();
m_ragdollNodeCard->hide();
m_jointLimitWidget->Update(QModelIndex());
m_jointLimitWidget->hide();
m_collidersWidget->hide();
}
}
else
{
m_ragdollNodeEditor->ClearInstances(true);
m_jointLimitWidget->Update(QModelIndex());
m_collidersWidget->Reset();
}
}
void RagdollNodeWidget::OnAddRemoveRagdollNode()
{
if (GetRagdollNodeConfig())
{
// The node is present in the ragdoll, remove it.
RagdollNodeInspectorPlugin::RemoveFromRagdoll(m_selectedModelIndices);
}
else
{
// The node is not part of the ragdoll, add it.
RagdollNodeInspectorPlugin::AddToRagdoll(m_selectedModelIndices);
}
}
void RagdollNodeWidget::OnAddCollider(const AZ::TypeId& colliderType)
{
ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::Ragdoll, colliderType);
}
void RagdollNodeWidget::OnCopyCollider(size_t colliderIndex)
{
ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll);
}
void RagdollNodeWidget::OnPasteCollider(size_t colliderIndex, bool replace)
{
ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll, replace);
}
void RagdollNodeWidget::OnRemoveCollider(size_t colliderIndex)
{
CommandColliderHelpers::RemoveCollider(GetActor()->GetID(), GetNode()->GetNameString(), PhysicsSetup::Ragdoll, colliderIndex);
}
Physics::RagdollConfiguration* RagdollNodeWidget::GetRagdollConfig() const
{
Actor* actor = GetActor();
Node* node = GetNode();
if (actor && node)
{
const AZStd::shared_ptr<EMotionFX::PhysicsSetup>& physicsSetup = actor->GetPhysicsSetup();
if (physicsSetup)
{
return &physicsSetup->GetRagdollConfig();
}
}
return nullptr;
}
Physics::CharacterColliderNodeConfiguration* RagdollNodeWidget::GetRagdollColliderNodeConfig() const
{
Actor* actor = GetActor();
Node* node = GetNode();
if (actor && node)
{
const AZStd::shared_ptr<EMotionFX::PhysicsSetup>& physicsSetup = actor->GetPhysicsSetup();
if (physicsSetup)
{
const Physics::CharacterColliderConfiguration& colliderConfig = physicsSetup->GetRagdollConfig().m_colliders;
return colliderConfig.FindNodeConfigByName(node->GetNameString());
}
}
return nullptr;
}
Physics::RagdollNodeConfiguration* RagdollNodeWidget::GetRagdollNodeConfig() const
{
Actor* actor = GetActor();
Node* node = GetNode();
if (actor && node)
{
const AZStd::shared_ptr<EMotionFX::PhysicsSetup>& physicsSetup = actor->GetPhysicsSetup();
if (physicsSetup)
{
const Physics::RagdollConfiguration& ragdollConfig = physicsSetup->GetRagdollConfig();
return ragdollConfig.FindNodeConfigByName(node->GetNameString());
}
}
return nullptr;
}
} // namespace EMotionFX