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/LmbrCentral/Code/Source/Shape/SplineComponent.cpp

345 lines
12 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 "SplineComponent.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace LmbrCentral
{
using SplineComboBoxVec = AZStd::vector<AZStd::pair<size_t, AZStd::string>>;
static SplineComboBoxVec PopulateSplineTypeList()
{
return SplineComboBoxVec
{
AZStd::make_pair(AZ::LinearSpline::RTTI_Type().GetHash(), "Linear"),
AZStd::make_pair(AZ::BezierSpline::RTTI_Type().GetHash(), "Bezier"),
AZStd::make_pair(AZ::CatmullRomSpline::RTTI_Type().GetHash(), "Catmull-Rom")
};
}
static AZ::SplinePtr MakeSplinePtr(AZ::u64 splineType)
{
if (splineType == AZ::LinearSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::LinearSpline>();
}
if (splineType == AZ::BezierSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::BezierSpline>();
}
if (splineType == AZ::CatmullRomSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::CatmullRomSpline>();
}
AZ_Assert(false, "Unhandled spline type %d in %s", splineType, __FUNCTION__);
return nullptr;
}
static AZ::SplinePtr CopySplinePtr(AZ::u64 splineType, const AZ::SplinePtr& spline)
{
if (splineType == AZ::LinearSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::LinearSpline>(*spline);
}
if (splineType == AZ::BezierSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::BezierSpline>(*spline);
}
if (splineType == AZ::CatmullRomSpline::RTTI_Type().GetHash())
{
return AZStd::make_shared<AZ::CatmullRomSpline>(*spline);
}
AZ_Assert(false, "Unhandled spline type %d in %s", splineType, __FUNCTION__);
return nullptr;
}
SplineCommon::SplineCommon()
{
m_spline = MakeSplinePtr(m_splineType);
}
void SplineCommon::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SplineCommon>()
->Version(1)
->Field("Spline Type", &SplineCommon::m_splineType)
->Field("Spline", &SplineCommon::m_spline);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<SplineCommon>("Configuration", "Spline configuration parameters")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
//->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) // disabled - prevents ChangeNotify attribute firing correctly
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &SplineCommon::m_splineType, "Spline Type", "Interpolation type to use between vertices.")
->Attribute(AZ::Edit::Attributes::EnumValues, &PopulateSplineTypeList)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &SplineCommon::OnChangeSplineType)
->DataElement(AZ::Edit::UIHandlers::Default, &SplineCommon::m_spline, "Spline", "Data representing the spline.")
//->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) // disabled - prevents ChangeNotify attribute firing correctly
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
}
}
}
void SplineCommon::ChangeSplineType(AZ::u64 splineType)
{
m_splineType = splineType;
OnChangeSplineType();
}
void SplineCommon::SetCallbacks(
const AZ::IndexFunction& OnAddVertex, const AZ::IndexFunction& OnRemoveVertex,
const AZ::IndexFunction& OnUpdateVertex, const AZ::VoidFunction& OnSetVertices,
const AZ::VoidFunction& OnClearVertices, const AZ::VoidFunction& OnChangeType,
const AZ::BoolFunction& OnOpenClose)
{
m_onAddVertex = OnAddVertex;
m_onRemoveVertex = OnRemoveVertex;
m_onUpdateVertex = OnUpdateVertex;
m_onSetVertices = OnSetVertices;
m_onClearVertices = OnClearVertices;
m_onChangeType = OnChangeType;
m_onOpenCloseChange = OnOpenClose;
m_spline->SetCallbacks(
OnAddVertex, OnRemoveVertex,
OnUpdateVertex, OnSetVertices,
OnClearVertices, OnOpenClose);
}
AZ::u32 SplineCommon::OnChangeSplineType()
{
AZ::u32 ret = AZ::Edit::PropertyRefreshLevels::None;
if (m_spline->RTTI_GetType().GetHash() != m_splineType)
{
m_spline = CopySplinePtr(m_splineType, m_spline);
m_spline->SetCallbacks(
m_onAddVertex, m_onRemoveVertex, m_onUpdateVertex,
m_onSetVertices, m_onClearVertices, m_onOpenCloseChange);
ret = AZ::Edit::PropertyRefreshLevels::EntireTree;
if (m_onChangeType)
{
m_onChangeType();
}
}
return ret;
}
/// BehaviorContext forwarder for SplineComponentNotificationBus
class BehaviorSplineComponentNotificationBusHandler
: public SplineComponentNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorSplineComponentNotificationBusHandler, "{05816EA4-A4F0-4FB4-A82B-D6537B215D25}", AZ::SystemAllocator, OnSplineChanged);
void OnSplineChanged() override
{
Call(FN_OnSplineChanged);
}
};
void SplineComponent::Reflect(AZ::ReflectContext* context)
{
SplineCommon::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SplineComponent, AZ::Component>()
->Version(1)
->Field("Configuration", &SplineComponent::m_splineCommon);
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<SplineComponentNotificationBus>("SplineComponentNotificationBus")->
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::All)->
Handler<BehaviorSplineComponentNotificationBusHandler>();
behaviorContext->EBus<SplineComponentRequestBus>("SplineComponentRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Edit::Attributes::Category, "Shape")
->Attribute(AZ::Script::Attributes::Module, "shape")
->Event("GetSpline", &SplineComponentRequestBus::Events::GetSpline)
->Event("SetClosed", &SplineComponentRequestBus::Events::SetClosed)
->Event("AddVertex", &SplineComponentRequestBus::Events::AddVertex)
->Event("UpdateVertex", &SplineComponentRequestBus::Events::UpdateVertex)
->Event("InsertVertex", &SplineComponentRequestBus::Events::InsertVertex)
->Event("RemoveVertex", &SplineComponentRequestBus::Events::RemoveVertex)
->Event("ClearVertices", &SplineComponentRequestBus::Events::ClearVertices);
}
}
void SplineComponent::Activate()
{
m_currentTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(m_currentTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
SplineComponentRequestBus::Handler::BusConnect(GetEntityId());
const auto splineChanged = [this]()
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnSplineChanged);
};
const auto vertexAdded = [this, splineChanged](size_t index)
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnVertexAdded, index);
splineChanged();
};
const auto vertexRemoved = [this, splineChanged](size_t index)
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnVertexRemoved, index);
splineChanged();
};
const auto vertexUpdated = [this, splineChanged](size_t index)
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnVertexUpdated, index);
splineChanged();
};
const auto verticesSet = [this, splineChanged]()
{
SplineComponentNotificationBus::Event(
GetEntityId(),
&SplineComponentNotificationBus::Events::OnVerticesSet,
m_splineCommon.m_spline->GetVertices()
);
splineChanged();
};
const auto verticesCleared = [this, splineChanged]()
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnVerticesCleared);
splineChanged();
};
const auto openCloseChanged = [this, splineChanged](const bool closed)
{
SplineComponentNotificationBus::Event(
GetEntityId(), &SplineComponentNotificationBus::Events::OnOpenCloseChanged, closed);
splineChanged();
};
m_splineCommon.SetCallbacks(
vertexAdded,
vertexRemoved,
vertexUpdated,
verticesSet,
verticesCleared,
splineChanged,
openCloseChanged);
}
void SplineComponent::Deactivate()
{
SplineComponentRequestBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
}
void SplineComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
{
m_currentTransform = world;
}
AZ::SplinePtr SplineComponent::GetSpline()
{
return m_splineCommon.m_spline;
}
void SplineComponent::ChangeSplineType(AZ::u64 splineType)
{
m_splineCommon.ChangeSplineType(splineType);
}
bool SplineComponent::UpdateVertex(size_t index, const AZ::Vector3& vertex)
{
return m_splineCommon.m_spline->m_vertexContainer.UpdateVertex(index, vertex);
}
bool SplineComponent::GetVertex(size_t index, AZ::Vector3& vertex) const
{
return m_splineCommon.m_spline->m_vertexContainer.GetVertex(index, vertex);
}
void SplineComponent::AddVertex(const AZ::Vector3& vertex)
{
m_splineCommon.m_spline->m_vertexContainer.AddVertex(vertex);
}
bool SplineComponent::InsertVertex(size_t index, const AZ::Vector3& vertex)
{
return m_splineCommon.m_spline->m_vertexContainer.InsertVertex(index, vertex);
}
bool SplineComponent::RemoveVertex(size_t index)
{
return m_splineCommon.m_spline->m_vertexContainer.RemoveVertex(index);
}
void SplineComponent::SetVertices(const AZStd::vector<AZ::Vector3>& vertices)
{
m_splineCommon.m_spline->m_vertexContainer.SetVertices(vertices);
}
void SplineComponent::ClearVertices()
{
m_splineCommon.m_spline->m_vertexContainer.Clear();
}
bool SplineComponent::Empty() const
{
return m_splineCommon.m_spline->m_vertexContainer.Empty();
}
size_t SplineComponent::Size() const
{
return m_splineCommon.m_spline->m_vertexContainer.Size();
}
void SplineComponent::SetClosed(const bool closed)
{
// set closed callback calls OnSplineChanged
m_splineCommon.m_spline->SetClosed(closed);
}
} // namespace LmbrCentral